@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/_exports/_internal.d.ts +11 -0
- package/dist/_exports/_internal.d.ts.map +1 -1
- package/dist/_exports/_internal.js +271 -6
- package/dist/_exports/_internal.js.map +1 -1
- package/dist/index.js +6 -515
- package/dist/index.js.map +1 -1
- package/dist/sentryBrowser-DT6hL7NZ.js +1 -0
- package/dist/version-DRnj2zr5.js +1072 -0
- package/dist/version-DRnj2zr5.js.map +1 -0
- package/package.json +3 -2
- package/src/_exports/_internal.ts +1 -0
- package/src/errorReporting/errorReporter.test.ts +207 -0
- package/src/errorReporting/errorReporter.ts +223 -0
- package/src/errorReporting/isErrorReportingEnabled.test.ts +67 -0
- package/src/errorReporting/isErrorReportingEnabled.ts +47 -0
- package/src/errorReporting/isKnownSdkError.test.ts +57 -0
- package/src/errorReporting/isKnownSdkError.ts +67 -0
- package/src/errorReporting/privacy.test.ts +67 -0
- package/src/errorReporting/privacy.ts +61 -0
- package/src/errorReporting/sentryBrowser.ts +10 -0
- package/src/errorReporting/sentryTypes.ts +7 -0
- package/dist/version-DfqE7Tzw.js +0 -559
- package/dist/version-DfqE7Tzw.js.map +0 -1
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
|
|
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
|
|
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,
|
|
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 {
|
|
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:
|
|
2503
|
+
documentId: releaseDocumentId,
|
|
3013
2504
|
patches: [action.patch],
|
|
3014
2505
|
transactionId,
|
|
3015
2506
|
timestamp,
|