@sanity/sdk 3.12.0 → 3.13.0-next.20261106104949

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/dist/index.js CHANGED
@@ -5,18 +5,17 @@ import { a as compareProjectOrganization, i as projects, n as threadComments, o
5
5
  import { r as omitProperty, t as isDeepEqual } from "./object-CxXlSVg7.js";
6
6
  import { n as getClient, r as getClientState } from "./clientStore-C20oRB2w.js";
7
7
  import { n as getDashboardOrganizationId } from "./topicStore-BKyvThWM.js";
8
- import { _ as updateLastLoadMoreRequest, a as resolveQuery, b as favorites, c as getAllReleasesState, d as getUsersKey, f as initializeRequest, g as setUsersError, h as setUsersData, l as addSubscription, m as removeSubscription, o as getPerspectiveState, p as parseUsersKey, r as getQueryState, s as getActiveReleasesState, t as CORE_SDK_VERSION, u as cancelRequest, v as PROJECT_API_VERSION, w as getProjectAccessErrorProjectId, x as setFavorite, y as USERS_STATE_CLEAR_DELAY } from "./version-DfqE7Tzw.js";
8
+ import { A as patchOperations, C as ActionError, D as createMutationApplier, E as checkGrant, N as getProjectAccessErrorProjectId, O as preserveNumericOperations, T as applySingleDocPatch, _ as updateLastLoadMoreRequest, a as resolveQuery, b as favorites, c as getAllReleasesState, d as getUsersKey, f as initializeRequest, g as setUsersError, h as setUsersData, j as processMutations, k as getId, l as addSubscription, m as removeSubscription, o as getPerspectiveState, p as parseUsersKey, r as getQueryState, s as getActiveReleasesState, t as CORE_SDK_VERSION, u as cancelRequest, v as PROJECT_API_VERSION, w as PermissionActionError, x as setFavorite, y as USERS_STATE_CLEAR_DELAY } from "./version-DRnj2zr5.js";
9
9
  import { ClientError, CorsOriginError } from "@sanity/client";
10
10
  import { EMPTY, NEVER, Observable, ReplaySubject, Subject, Subscription, auditTime, catchError, combineLatest, concat, concatMap, debounceTime, defer, distinctUntilChanged, filter, first, firstValueFrom, from, fromEvent, groupBy, map, merge, mergeMap, of, pairwise, partition, race, share, skip, startWith, switchMap, take, takeUntil, tap, throttle, throwError, timer, withLatestFrom } from "rxjs";
11
- import { isKeySegment, isKeyedObject } from "@sanity/types";
11
+ import "@sanity/types";
12
12
  import { createSelector } from "reselect";
13
13
  import { DocumentId, getDraftId, getPublishedId, getVersionId, isDraftId, isPublishedId, isVersionId } from "@sanity/id-utils";
14
- import { getIndexForKey, getIndexForKey as getIndexForKey$1, getPathDepth, joinPaths, jsonMatch, jsonMatch as jsonMatch$1, slicePath, slicePath as slicePath$1, stringifyPath, stringifyPath as stringifyPath$1 } from "@sanity/json-match";
14
+ import { getIndexForKey, getPathDepth, joinPaths, jsonMatch, jsonMatch as jsonMatch$1, slicePath, stringifyPath } from "@sanity/json-match";
15
15
  import { SanityEncoder } from "@sanity/mutate";
16
16
  import { createDocumentLoaderFromClient } from "@sanity/mutate/_unstable_store";
17
- import { evaluateSync, parse } from "groq-js";
17
+ import { parse } from "groq-js";
18
18
  import { catchError as catchError$1, distinctUntilChanged as distinctUntilChanged$1, map as map$1, mergeMap as mergeMap$1, retry as retry$1, scan, share as share$1, switchMap as switchMap$1 } from "rxjs/operators";
19
- import { applyPatches, parsePatch } from "@sanity/diff-match-patch";
20
19
  import { diffValue } from "@sanity/diff-patch";
21
20
  import { fromUrl } from "@sanity/bifur-client";
22
21
  /**
@@ -1751,352 +1750,6 @@ const RELEASE_ACTION_TYPES = /* @__PURE__ */ new Set([
1751
1750
  function isReleaseAction(action) {
1752
1751
  return RELEASE_ACTION_TYPES.has(action.type);
1753
1752
  }
1754
- function generateArrayKey(length = 12) {
1755
- let numBytes = Math.ceil(length / 2), bytes = crypto.getRandomValues(new Uint8Array(numBytes));
1756
- return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, length);
1757
- }
1758
- function memoize(fn) {
1759
- let cache = /* @__PURE__ */ new WeakMap();
1760
- return ((input) => {
1761
- if (!input || typeof input != "object") return fn(input);
1762
- let cached = cache.get(input);
1763
- if (cached) return cached;
1764
- let result = fn(input);
1765
- return cache.set(input, result), result;
1766
- });
1767
- }
1768
- /**
1769
- * Recursively traverse a value. When an array is encountered, ensure that
1770
- * each object item has a _key property. Memoized such that sub-objects that
1771
- * have not changed aren't re-computed.
1772
- */
1773
- const ensureArrayKeysDeep = memoize((input) => {
1774
- if (!input || typeof input != "object") return input;
1775
- if (Array.isArray(input)) return !input.length || typeof input[0] != "object" || input.every(isKeyedObject) ? input : input.map((item) => !item || typeof item != "object" ? item : isKeyedObject(item) ? ensureArrayKeysDeep(item) : {
1776
- ...ensureArrayKeysDeep(item),
1777
- _key: generateArrayKey()
1778
- });
1779
- let entries = Object.entries(input).map(([key, value]) => [key, ensureArrayKeysDeep(value)]);
1780
- return entries.every(([key, value]) => input[key] === value) ? input : Object.fromEntries(entries);
1781
- });
1782
- /**
1783
- * Whether every array-addressing segment (keyed `[_key=="…"]` or numeric
1784
- * index) in the given match path resolves to an existing array item.
1785
- *
1786
- * This mirrors Content Lake, which auto-creates missing *object* properties
1787
- * when setting but never creates *array items*:
1788
- *
1789
- * ```js
1790
- * // object properties are created, even nested ones:
1791
- * set({}, {'meta.author.name': 'Ada'})
1792
- * // -> {meta: {author: {name: 'Ada'}}} (applies)
1793
- *
1794
- * // array items are not:
1795
- * set({items: [{_key: 'a'}]}, {'items[_key=="b"].title': 'x'})
1796
- * // -> {items: [{_key: 'a'}]} (no-op)
1797
- * ```
1798
- *
1799
- * Without this filter, the second example fabricated a degenerate item
1800
- * (`{title: 'x'}`, no `_key`) that would never exist on the server, which
1801
- * is how concurrent Portable Text edits grew phantom span-less children.
1802
- */
1803
- function resolvesArraySegments(input, path) {
1804
- let current = input;
1805
- for (let segment of path) if (typeof segment == "string") current = typeof current == "object" && current && !Array.isArray(current) ? current[segment] : void 0;
1806
- else {
1807
- if (!Array.isArray(current)) return !1;
1808
- if (isKeySegment(segment)) {
1809
- let index = getIndexForKey$1(current, segment._key);
1810
- if (index === void 0) return !1;
1811
- current = current[index];
1812
- } else {
1813
- let index = segment < 0 ? current.length + segment : segment;
1814
- if (!(index in current)) return !1;
1815
- current = current[index];
1816
- }
1817
- }
1818
- return !0;
1819
- }
1820
- function set(input, pathExpressionValues) {
1821
- let result = Object.entries(pathExpressionValues).flatMap(([pathExpression, replacementValue]) => Array.from(jsonMatch$1(input, pathExpression)).map((matchEntry) => ({
1822
- ...matchEntry,
1823
- replacementValue
1824
- }))).filter(({ path }) => resolvesArraySegments(input, path)).reduce((acc, { path, replacementValue }) => setDeep(acc, path, replacementValue), input);
1825
- return ensureArrayKeysDeep(result);
1826
- }
1827
- function setIfMissing(input, pathExpressionValues) {
1828
- let result = Object.entries(pathExpressionValues).flatMap(([pathExpression, replacementValue]) => Array.from(jsonMatch$1(input, pathExpression)).map((matchEntry) => ({
1829
- ...matchEntry,
1830
- replacementValue
1831
- }))).filter((matchEntry) => matchEntry.value === null || matchEntry.value === void 0).filter(({ path }) => resolvesArraySegments(input, path)).reduce((acc, { path, replacementValue }) => setDeep(acc, path, replacementValue), input);
1832
- return ensureArrayKeysDeep(result);
1833
- }
1834
- function unset(input, pathExpressions) {
1835
- let result = pathExpressions.flatMap((pathExpression) => Array.from(jsonMatch$1(input, pathExpression))).reverse().reduce((acc, { path }) => unsetDeep(acc, path), input);
1836
- return ensureArrayKeysDeep(result);
1837
- }
1838
- function insert(input, { items, ...insertPatch }) {
1839
- let operation, pathExpression;
1840
- if ("before" in insertPatch ? (operation = "before", pathExpression = insertPatch.before) : "after" in insertPatch ? (operation = "after", pathExpression = insertPatch.after) : "replace" in insertPatch && (operation = "replace", pathExpression = insertPatch.replace), !operation || typeof pathExpression != "string" || !pathExpression.length) return input;
1841
- let arrayPath = slicePath$1(pathExpression, 0, -1), positionPath = slicePath$1(pathExpression, -1), result = input;
1842
- for (let { path, value } of jsonMatch$1(input, arrayPath)) {
1843
- if (!Array.isArray(value)) continue;
1844
- let arr = value;
1845
- switch (operation) {
1846
- case "replace": {
1847
- let indexesToRemove = /* @__PURE__ */ new Set(), position = Infinity;
1848
- for (let itemMatch of jsonMatch$1(arr, positionPath)) {
1849
- if (itemMatch.path.length !== 1) continue;
1850
- let [segment] = itemMatch.path;
1851
- if (typeof segment == "string") continue;
1852
- let index;
1853
- typeof segment == "number" && (index = segment), typeof index == "number" && index < 0 && (index = arr.length + index), isKeySegment(segment) && (index = getIndexForKey$1(arr, segment._key)), typeof index == "number" && (index < 0 && (index = arr.length + index), indexesToRemove.add(index), index < position && (position = index));
1854
- }
1855
- if (position === Infinity) continue;
1856
- arr = arr.map((item, index) => ({
1857
- item,
1858
- index
1859
- })).filter(({ index }) => !indexesToRemove.has(index)).map(({ item }) => item), arr = [
1860
- ...arr.slice(0, position),
1861
- ...items,
1862
- ...arr.slice(position, arr.length)
1863
- ];
1864
- break;
1865
- }
1866
- case "before": {
1867
- let position = Infinity;
1868
- for (let itemMatch of jsonMatch$1(arr, positionPath)) {
1869
- if (itemMatch.path.length !== 1) continue;
1870
- let [segment] = itemMatch.path;
1871
- if (typeof segment == "string") continue;
1872
- let index;
1873
- typeof segment == "number" && (index = segment), typeof index == "number" && index < 0 && (index = arr.length + index), isKeySegment(segment) && (index = getIndexForKey$1(arr, segment._key)), typeof index == "number" && (index < 0 && (index = arr.length - index), index < position && (position = index));
1874
- }
1875
- if (position === Infinity) continue;
1876
- arr = [
1877
- ...arr.slice(0, position),
1878
- ...items,
1879
- ...arr.slice(position, arr.length)
1880
- ];
1881
- break;
1882
- }
1883
- case "after": {
1884
- let position = -Infinity;
1885
- for (let itemMatch of jsonMatch$1(arr, positionPath)) {
1886
- if (itemMatch.path.length !== 1) continue;
1887
- let [segment] = itemMatch.path;
1888
- if (typeof segment == "string") continue;
1889
- let index;
1890
- typeof segment == "number" && (index = segment), typeof index == "number" && index < 0 && (index = arr.length + index), isKeySegment(segment) && (index = getIndexForKey$1(arr, segment._key)), typeof index == "number" && index > position && (position = index);
1891
- }
1892
- if (position === -Infinity) continue;
1893
- arr = [
1894
- ...arr.slice(0, position + 1),
1895
- ...items,
1896
- ...arr.slice(position + 1, arr.length)
1897
- ];
1898
- break;
1899
- }
1900
- default: continue;
1901
- }
1902
- result = setDeep(result, path, arr);
1903
- }
1904
- return ensureArrayKeysDeep(result);
1905
- }
1906
- function inc(input, pathExpressionValues) {
1907
- let result = Object.entries(pathExpressionValues).flatMap(([pathExpression, valueToAdd]) => Array.from(jsonMatch$1(input, pathExpression)).map((matchEntry) => ({
1908
- ...matchEntry,
1909
- valueToAdd
1910
- }))).filter((matchEntry) => typeof matchEntry.value == "number").reduce((acc, { path, value, valueToAdd }) => setDeep(acc, path, value + valueToAdd), input);
1911
- return ensureArrayKeysDeep(result);
1912
- }
1913
- function dec(input, pathExpressionValues) {
1914
- let result = inc(input, Object.fromEntries(Object.entries(pathExpressionValues).filter(([, value]) => typeof value == "number").map(([key, value]) => [key, -value])));
1915
- return ensureArrayKeysDeep(result);
1916
- }
1917
- function diffMatchPatch(input, pathExpressionValues) {
1918
- let result = Object.entries(pathExpressionValues).flatMap(([pathExpression, dmp]) => Array.from(jsonMatch$1(input, pathExpression)).map((m) => ({
1919
- ...m,
1920
- dmp
1921
- }))).filter((i) => i.value !== void 0).map(({ path, value, dmp }) => {
1922
- if (typeof value != "string") throw Error(`Can't diff-match-patch \`${JSON.stringify(value)}\` at path \`${stringifyPath$1(path)}\`, because it is not a string`);
1923
- let [nextValue] = applyPatches(parsePatch(dmp), value, { allowExceedingIndices: !0 });
1924
- return {
1925
- path,
1926
- value: nextValue
1927
- };
1928
- }).reduce((acc, { path, value }) => setDeep(acc, path, value), input);
1929
- return ensureArrayKeysDeep(result);
1930
- }
1931
- function ifRevisionID(input, revisionId) {
1932
- let inputRev = typeof input == "object" && input && "_rev" in input && typeof input._rev == "string" ? input._rev : void 0;
1933
- if (typeof inputRev != "string") throw Error("Patch specified `ifRevisionID` but could not find document's revision ID.");
1934
- if (revisionId !== inputRev) throw Error(`Patch's \`ifRevisionID\` \`${revisionId}\` does not match document's revision ID \`${inputRev}\``);
1935
- return input;
1936
- }
1937
- function setDeep(input, path, value) {
1938
- let [currentSegment, ...restOfPath] = path;
1939
- if (currentSegment === void 0) return value;
1940
- if (typeof input != "object" || !input) {
1941
- if (typeof currentSegment == "string") return { [currentSegment]: setDeep(null, restOfPath, value) };
1942
- let index;
1943
- if (isKeySegment(currentSegment)) index = 0;
1944
- else if (typeof currentSegment == "number" && currentSegment >= 0) index = currentSegment;
1945
- else return input;
1946
- return [...Array.from({ length: index }).fill(null), setDeep(null, restOfPath, value)];
1947
- }
1948
- if (Array.isArray(input)) {
1949
- let index;
1950
- return isKeySegment(currentSegment) ? index = getIndexForKey$1(input, currentSegment._key) ?? input.length : typeof currentSegment == "number" && (index = currentSegment < 0 ? input.length + currentSegment : currentSegment), index === void 0 ? input : index in input ? input.map((nestedInput, i) => i === index ? setDeep(nestedInput, restOfPath, value) : nestedInput) : [
1951
- ...input,
1952
- ...Array.from({ length: index - input.length }).fill(null),
1953
- setDeep(null, restOfPath, value)
1954
- ];
1955
- }
1956
- return typeof currentSegment == "object" ? input : currentSegment in input ? Object.fromEntries(Object.entries(input).map(([key, nestedInput]) => key === currentSegment ? [key, setDeep(nestedInput, restOfPath, value)] : [key, nestedInput])) : {
1957
- ...input,
1958
- [currentSegment]: setDeep(null, restOfPath, value)
1959
- };
1960
- }
1961
- function unsetDeep(input, path) {
1962
- let [currentSegment, ...restOfPath] = path;
1963
- if (currentSegment === void 0 || typeof input != "object" || !input) return input;
1964
- let _segment;
1965
- if (isKeySegment(currentSegment) ? _segment = getIndexForKey$1(input, currentSegment._key) : (typeof currentSegment == "string" || typeof currentSegment == "number") && (_segment = currentSegment), _segment === void 0) return input;
1966
- let segment = _segment;
1967
- return typeof segment == "number" && Array.isArray(input) && (segment = segment < 0 ? input.length + segment : segment), segment in input ? restOfPath.length ? Array.isArray(input) ? input.map((nestedInput, index) => index === segment ? unsetDeep(nestedInput, restOfPath) : nestedInput) : Object.fromEntries(Object.entries(input).map(([key, value]) => key === segment ? [key, unsetDeep(value, restOfPath)] : [key, value])) : Array.isArray(input) ? input.filter((_nestedInput, index) => index !== segment) : Object.fromEntries(Object.entries(input).filter(([key]) => key !== segment.toString())) : input;
1968
- }
1969
- /**
1970
- * Exposes the patch operations used by {@link processMutations}.
1971
- *
1972
- * @remarks
1973
- * Property order determines execution order in {@link processMutations}:
1974
- * `ifRevisionID`, `set`, `setIfMissing`, `unset`, `inc`, `dec`, `insert`,
1975
- * then `diffMatchPatch`. Reordering the properties changes mutation results.
1976
- * Freezes the collection to prevent callers from replacing the functions
1977
- * used by the SDK's mutation evaluator.
1978
- *
1979
- * @see https://www.sanity.io/docs/http-mutations#5b4db1396e56
1980
- *
1981
- * @example
1982
- * ```ts
1983
- * const document = {items: [{_key: 'a', title: 'Before'}]}
1984
- * const updated = patchOperations.set<typeof document>(document, {
1985
- * 'items[_key=="a"].title': 'After',
1986
- * })
1987
- * ```
1988
- *
1989
- * @internal
1990
- */
1991
- const patchOperations = Object.freeze({
1992
- ifRevisionID,
1993
- set,
1994
- setIfMissing,
1995
- unset,
1996
- inc,
1997
- dec,
1998
- insert,
1999
- diffMatchPatch
2000
- });
2001
- /**
2002
- * Implements ID generation:
2003
- *
2004
- * A create mutation creates a new document. It takes the literal document
2005
- * content as its argument. The rules for the new document's identifier are as
2006
- * follows:
2007
- *
2008
- * - If the `_id` attribute is missing, then a new, random, unique ID is
2009
- * generated.
2010
- * - If the `_id` attribute is present but ends with `.`, then it is used as a
2011
- * prefix for a new, random, unique ID.
2012
- * - If the _id attribute is present, it is used as-is.
2013
- *
2014
- * [- source](https://www.sanity.io/docs/http-mutations#c732f27330a4)
2015
- */
2016
- function getId(id) {
2017
- return !id || typeof id != "string" ? randomUuid() : id.endsWith(".") ? `${id}${randomUuid()}` : id;
2018
- }
2019
- function getDocumentIds(selection) {
2020
- if ("id" in selection) {
2021
- let ids = (Array.isArray(selection.id) ? selection.id : [selection.id]).filter((id) => typeof id == "string");
2022
- return Array.from(new Set(ids));
2023
- }
2024
- if ("query" in selection) throw Error("'query' in mutations is not supported.");
2025
- return [];
2026
- }
2027
- /**
2028
- * Applies mutations to an in-memory set of Sanity documents.
2029
- * Deleted documents remain in the returned set with a value of `null`.
2030
- *
2031
- * Include every document affected by `mutations` in `documents`, using `null`
2032
- * for documents that do not exist.
2033
- *
2034
- * Returns `documents` itself when `mutations` is empty. Otherwise, returns a
2035
- * shallow copy of that object. The result can reuse document objects and
2036
- * nested objects from `documents`. Mutating a reused object through the
2037
- * result also changes the input.
2038
- *
2039
- * Sets `_rev` to `transactionId` on created, replaced, and patched documents.
2040
- * Uses `timestamp` for timestamps unless the mutation preserves them.
2041
- * Ignores `merge` patches; `inc` and `dec` skip values that are not numbers.
2042
- * Generates document IDs when omitted or ending in `.`.
2043
- * Patch operations can generate missing array keys.
2044
- *
2045
- * Does not persist documents or run Content Lake's server validation.
2046
- *
2047
- * @throws If a selection uses a query, a `create` targets an existing document,
2048
- * or a `patch` targets a document that does not exist.
2049
- * @throws If a patch operation fails, including a revision check failure or
2050
- * `diffMatchPatch` applied to a value other than a string or `undefined`.
2051
- *
2052
- * @internal
2053
- */
2054
- function processMutations({ documents, mutations, transactionId, timestamp }) {
2055
- if (!mutations.length) return documents;
2056
- let dataset = { ...documents }, now = timestamp || (/* @__PURE__ */ new Date()).toISOString();
2057
- for (let mutation of mutations) if ("create" in mutation) {
2058
- let id = getId(mutation.create._id);
2059
- if (dataset[id]) throw Error(`Cannot create document with \`_id\` \`${id}\` because another document with the same ID already exists.`);
2060
- dataset[id] = {
2061
- _createdAt: now,
2062
- _updatedAt: now,
2063
- ...mutation.create,
2064
- _rev: transactionId,
2065
- _id: id
2066
- };
2067
- } else if ("createOrReplace" in mutation) {
2068
- let id = getId(mutation.createOrReplace._id), prev = dataset[id];
2069
- dataset[id] = {
2070
- ...mutation.createOrReplace,
2071
- _createdAt: prev?._createdAt || typeof mutation.createOrReplace._createdAt == "string" && mutation.createOrReplace._createdAt || now,
2072
- _updatedAt: prev ? now : typeof mutation.createOrReplace._updatedAt == "string" && mutation.createOrReplace._updatedAt || now,
2073
- _rev: transactionId,
2074
- _id: id
2075
- };
2076
- } else if ("createIfNotExists" in mutation) {
2077
- let id = getId(mutation.createIfNotExists._id);
2078
- if (dataset[id]) continue;
2079
- dataset[id] = {
2080
- _createdAt: now,
2081
- _updatedAt: now,
2082
- ...mutation.createIfNotExists,
2083
- _rev: transactionId,
2084
- _id: id
2085
- };
2086
- } else if ("delete" in mutation) for (let id of getDocumentIds(mutation.delete)) dataset[id] = null;
2087
- else if ("patch" in mutation) {
2088
- let { patch } = mutation, patched = getDocumentIds(patch).map((id) => {
2089
- if (!dataset[id]) throw Error(`Cannot patch document with ID \`${id}\` because it was not found.`);
2090
- return Object.entries(patchOperations).reduce((acc, [type, operation]) => patch[type] ? operation(acc, patch[type]) : acc, dataset[id]);
2091
- });
2092
- for (let result of patched) dataset[result._id] = {
2093
- ...result,
2094
- _rev: transactionId,
2095
- _updatedAt: now
2096
- };
2097
- }
2098
- return dataset;
2099
- }
2100
1753
  var OutOfSyncError = class extends Error {
2101
1754
  /**
2102
1755
  * Attach state to the error for debugging/reporting
@@ -2282,168 +1935,6 @@ var MultiKeyWeakMap = class MultiKeyWeakMap {
2282
1935
  this.#setDeep(arrangedKeys, this.#rootMap, value);
2283
1936
  }
2284
1937
  };
2285
- function checkGrant(grantExpr, document, identity) {
2286
- let value = evaluateSync(grantExpr, {
2287
- params: { document },
2288
- identity
2289
- });
2290
- return value.type === "boolean" && value.data;
2291
- }
2292
- /**
2293
- * Thrown when a precondition for an action failed.
2294
- */
2295
- var ActionError = class extends Error {
2296
- documentId;
2297
- transactionId;
2298
- constructor(options) {
2299
- super(options.message), Object.assign(this, options);
2300
- }
2301
- }, PermissionActionError = class extends ActionError {};
2302
- /**
2303
- * Creates a `processMutations` wrapper that surfaces application failures as
2304
- * `ActionError`s when the caller asked to preserve their patch operations.
2305
- * With preserved operations, patches can legitimately fail to apply (e.g.
2306
- * re-applied onto a diverged document during a rebase), so wrapping lets a
2307
- * rebase skip the transaction instead of failing the store. Without
2308
- * `preserveOperations`, errors are rethrown untouched.
2309
- */
2310
- function createMutationApplier(options) {
2311
- let { documentId, transactionId, timestamp, preserveOperations } = options;
2312
- return (documents, mutations, documentSetName) => {
2313
- try {
2314
- return processMutations({
2315
- documents,
2316
- transactionId,
2317
- mutations,
2318
- timestamp
2319
- });
2320
- } catch (error) {
2321
- throw preserveOperations ? new ActionError({
2322
- documentId,
2323
- transactionId,
2324
- message: `Failed to apply patches to the ${documentSetName} document: ${error instanceof Error ? error.message : "Unknown error"}`
2325
- }) : error;
2326
- }
2327
- };
2328
- }
2329
- /**
2330
- * Re-applies the operational intent of user-supplied `inc`/`dec` patches to a
2331
- * set of snapshot-diffed patches.
2332
- *
2333
- * The outgoing patches sent to Content Lake are re-derived by diffing the
2334
- * base document before and after the user's patches were applied. That diff
2335
- * collapses an `inc` into a `set` of the resulting number, which turns
2336
- * concurrent increments from different clients into last-write-wins. This
2337
- * helper finds diffed `set` operations that are exactly explained by the
2338
- * user's `inc`/`dec` operations and swaps them back, so the server applies
2339
- * the increment against whatever value is current at commit time.
2340
- *
2341
- * A `set` is only swapped when its value equals the base value plus the
2342
- * accumulated user deltas for that exact path; anything else (e.g. an `inc`
2343
- * combined with a `set` of the same field) keeps the diffed `set`.
2344
- *
2345
- * Content Lake fails the whole transaction when `inc`/`dec` targets a missing
2346
- * path (unlike `set`, which always succeeds). To avoid turning a concurrent
2347
- * field removal into a transaction revert, the emitted patch seeds the target
2348
- * with a `setIfMissing` of the base value; the server executes `setIfMissing`
2349
- * before `inc`/`dec` within a single patch, so a concurrently removed field
2350
- * degrades to the same result the diffed `set` would have produced. A path
2351
- * into a concurrently removed keyed array item can still fail the
2352
- * transaction (`setIfMissing` cannot create array items); the transaction is
2353
- * then reverted and reported, which is preferable to silently resurrecting
2354
- * the item.
2355
- */
2356
- function preserveNumericOperations(baseBefore, userPatches, diffedPatches) {
2357
- if (!baseBefore || !userPatches?.length) return diffedPatches;
2358
- let targets = /* @__PURE__ */ new Map();
2359
- for (let patch of userPatches) for (let [op, sign] of [["inc", 1], ["dec", -1]]) {
2360
- let record = patch[op];
2361
- if (record) {
2362
- for (let [pathExpression, amount] of Object.entries(record)) if (typeof amount == "number") for (let match of jsonMatch$1(baseBefore, pathExpression)) {
2363
- let path = stringifyPath$1(match.path), existing = targets.get(path);
2364
- existing ? (existing.expectedValue += sign * amount, existing.delta += sign * amount) : typeof match.value == "number" && targets.set(path, {
2365
- baseValue: match.value,
2366
- expectedValue: match.value + sign * amount,
2367
- delta: sign * amount
2368
- });
2369
- }
2370
- }
2371
- }
2372
- if (!targets.size) return diffedPatches;
2373
- let preserved = /* @__PURE__ */ new Map(), next = diffedPatches.flatMap((patch) => {
2374
- if (!patch.set) return [patch];
2375
- let keptSet = {};
2376
- for (let [path, value] of Object.entries(patch.set)) {
2377
- let target = targets.get(path);
2378
- target && typeof value == "number" && value === target.expectedValue ? preserved.set(path, {
2379
- delta: target.delta,
2380
- baseValue: target.baseValue
2381
- }) : keptSet[path] = value;
2382
- }
2383
- if (Object.keys(keptSet).length === Object.keys(patch.set).length) return [patch];
2384
- let { set: _set, ...rest } = patch, withKept = Object.keys(keptSet).length ? {
2385
- ...rest,
2386
- set: keptSet
2387
- } : rest;
2388
- return Object.keys(withKept).length ? [withKept] : [];
2389
- });
2390
- if (!preserved.size) return next;
2391
- let setIfMissing = {}, inc = {}, dec = {};
2392
- for (let [path, { delta, baseValue }] of preserved) setIfMissing[path] = baseValue, delta >= 0 ? inc[path] = delta : dec[path] = -delta;
2393
- return [...next, {
2394
- setIfMissing,
2395
- ...Object.keys(inc).length && { inc },
2396
- ...Object.keys(dec).length && { dec }
2397
- }];
2398
- }
2399
- /**
2400
- * Shared logic for applying user-provided patches to a single document that
2401
- * is identified by an exact ID (no draft/published wrapping). Used by the
2402
- * liveEdit branch of `document.edit` and by `release.edit`.
2403
- *
2404
- * Returns the updated base + working sets, plus the diffed patches in both
2405
- * raw and mutation form so the caller can decide what to send to the server.
2406
- */
2407
- function applySingleDocPatch({ base: initialBase, working: initialWorking, documentId, patches, transactionId, timestamp, grants, identity, notFoundMessage = "Cannot edit document because it does not exist.", permissionMessage = `You do not have permission to edit document "${documentId}".`, preserveOperations }) {
2408
- let base = initialBase, working = initialWorking, userPatches = patches?.map((patch) => ({ patch: {
2409
- id: documentId,
2410
- ...patch
2411
- } }));
2412
- if (!userPatches?.length) return {
2413
- base,
2414
- working,
2415
- diffedPatches: [],
2416
- workingMutations: []
2417
- };
2418
- if (!working[documentId] || !base[documentId]) throw new ActionError({
2419
- documentId,
2420
- transactionId,
2421
- message: notFoundMessage
2422
- });
2423
- let applyMutations = createMutationApplier({
2424
- documentId,
2425
- transactionId,
2426
- timestamp,
2427
- preserveOperations
2428
- }), baseBefore = base[documentId];
2429
- base = applyMutations(base, userPatches, "base");
2430
- let baseAfter = base[documentId], diffedPatches = preserveOperations ? patches : preserveNumericOperations(baseBefore, patches, diffValue(baseBefore, baseAfter)), workingBefore = working[documentId];
2431
- if (!checkGrant(grants.update, workingBefore, identity)) throw new PermissionActionError({
2432
- documentId,
2433
- transactionId,
2434
- message: permissionMessage
2435
- });
2436
- let workingMutations = diffedPatches.map((patch) => ({ patch: {
2437
- id: documentId,
2438
- ...patch
2439
- } }));
2440
- return working = applyMutations(working, workingMutations, "working"), {
2441
- base,
2442
- working,
2443
- diffedPatches,
2444
- workingMutations
2445
- };
2446
- }
2447
1938
  function createGrantsLookup(datasetAcl) {
2448
1939
  let filtersByGrant = {
2449
1940
  create: /* @__PURE__ */ new Set(),
@@ -3006,10 +2497,10 @@ function handleReleaseDelete(action, ctx) {
3006
2497
  };
3007
2498
  }
3008
2499
  function handleReleaseEdit(action, ctx) {
3009
- let { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx, { base, working } = ctx, result = applySingleDocPatch({
2500
+ let { transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity } = ctx, { base, working } = ctx, releaseDocumentId = getReleaseDocumentId(action.releaseId), result = applySingleDocPatch({
3010
2501
  base,
3011
2502
  working,
3012
- documentId: getReleaseDocumentId(action.releaseId),
2503
+ documentId: releaseDocumentId,
3013
2504
  patches: [action.patch],
3014
2505
  transactionId,
3015
2506
  timestamp,