@sanity/mutator 6.7.0-next.36 → 6.7.0-next.5

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/lib/index.js CHANGED
@@ -2,1720 +2,1863 @@ import isEqual from "lodash-es/isEqual.js";
2
2
  import debugIt from "debug";
3
3
  import compact from "lodash-es/compact.js";
4
4
  import flatten from "lodash-es/flatten.js";
5
- import { applyPatches, makePatches, parsePatch, stringifyPatches } from "@sanity/diff-match-patch";
5
+ import { parsePatch as parsePatch$1, applyPatches, stringifyPatches, makePatches } from "@sanity/diff-match-patch";
6
6
  import max from "lodash-es/max.js";
7
7
  import min from "lodash-es/min.js";
8
8
  import { uuid } from "@sanity/uuid";
9
9
  const debug = debugIt("mutator-document");
10
10
  function isRecord$1(value) {
11
- return typeof value == "object" && !!value;
11
+ return value !== null && typeof value == "object";
12
12
  }
13
13
  const IS_DOTTABLE = /^[a-z_$]+/;
14
- /**
15
- * Converts a path in array form to a JSONPath string
16
- *
17
- * @param pathArray - Array of path segments
18
- * @returns String representation of the path
19
- * @internal
20
- */
21
14
  function arrayToJSONMatchPath(pathArray) {
22
- let path = "";
23
- return pathArray.forEach((segment, index) => {
24
- path += stringifySegment(segment, index === 0);
25
- }), path;
15
+ let path = "";
16
+ return pathArray.forEach((segment, index) => {
17
+ path += stringifySegment(segment, index === 0);
18
+ }), path;
26
19
  }
27
20
  function stringifySegment(segment, hasLeading) {
28
- if (typeof segment == "number") return `[${segment}]`;
29
- if (isRecord$1(segment)) {
30
- let seg = segment;
31
- return Object.keys(segment).map((key) => isPrimitiveValue(seg[key]) ? `[${key}=="${seg[key]}"]` : "").join("");
32
- }
33
- return typeof segment == "string" && IS_DOTTABLE.test(segment) ? hasLeading ? segment : `.${segment}` : `['${segment}']`;
21
+ if (typeof segment == "number")
22
+ return `[${segment}]`;
23
+ if (isRecord$1(segment)) {
24
+ const seg = segment;
25
+ return Object.keys(segment).map((key) => isPrimitiveValue(seg[key]) ? `[${key}=="${seg[key]}"]` : "").join("");
26
+ }
27
+ return typeof segment == "string" && IS_DOTTABLE.test(segment) ? hasLeading ? segment : `.${segment}` : `['${segment}']`;
34
28
  }
35
29
  function isPrimitiveValue(val) {
36
- switch (typeof val) {
37
- case "number":
38
- case "string":
39
- case "boolean": return !0;
40
- default: return !1;
41
- }
30
+ switch (typeof val) {
31
+ case "number":
32
+ case "string":
33
+ case "boolean":
34
+ return !0;
35
+ default:
36
+ return !1;
37
+ }
42
38
  }
43
- /**
44
- * Splits an expression into a set of heads, tails. A head is the next leaf node to
45
- * check for matches, and a tail is everything that follows it. Matching is done by
46
- * matching heads, then proceedint to the matching value, splitting the tail into
47
- * heads and tails and checking the heads against the new value, and so on.
48
- */
49
39
  function descend$1(tail) {
50
- let [head, newTail] = splitIfPath(tail);
51
- if (!head) throw Error("Head cannot be null");
52
- return spreadIfUnionHead(head, newTail);
40
+ const [head, newTail] = splitIfPath(tail);
41
+ if (!head)
42
+ throw new Error("Head cannot be null");
43
+ return spreadIfUnionHead(head, newTail);
53
44
  }
54
45
  function splitIfPath(tail) {
55
- if (tail.type !== "path") return [tail, null];
56
- let nodes = tail.nodes;
57
- return nodes.length === 0 ? [null, null] : nodes.length === 1 ? [nodes[0], null] : [nodes[0], {
58
- type: "path",
59
- nodes: nodes.slice(1)
60
- }];
46
+ if (tail.type !== "path")
47
+ return [tail, null];
48
+ const nodes = tail.nodes;
49
+ return nodes.length === 0 ? [null, null] : nodes.length === 1 ? [nodes[0], null] : [nodes[0], { type: "path", nodes: nodes.slice(1) }];
61
50
  }
62
51
  function concatPaths(path1, path2) {
63
- if (!path1 && !path2) return null;
64
- let nodes1 = path1 ? path1.nodes : [], nodes2 = path2 ? path2.nodes : [];
65
- return {
66
- type: "path",
67
- nodes: nodes1.concat(nodes2)
68
- };
52
+ if (!path1 && !path2)
53
+ return null;
54
+ const nodes1 = path1 ? path1.nodes : [], nodes2 = path2 ? path2.nodes : [];
55
+ return {
56
+ type: "path",
57
+ nodes: nodes1.concat(nodes2)
58
+ };
69
59
  }
70
60
  function spreadIfUnionHead(head, tail) {
71
- return head.type === "union" ? head.nodes.map((node) => {
72
- if (node.type === "path") {
73
- let [subHead, subTail] = splitIfPath(node);
74
- return [subHead, concatPaths(subTail, tail)];
75
- }
76
- return [node, tail];
77
- }) : [[head, tail]];
61
+ return head.type !== "union" ? [[head, tail]] : head.nodes.map((node) => {
62
+ if (node.type === "path") {
63
+ const [subHead, subTail] = splitIfPath(node);
64
+ return [subHead, concatPaths(subTail, tail)];
65
+ }
66
+ return [node, tail];
67
+ });
78
68
  }
79
69
  const digitChar = /[0-9]/, attributeCharMatcher = /^[a-zA-Z0-9_]$/, attributeFirstCharMatcher = /^[a-zA-Z_]$/, symbols = {
80
- operator: [
81
- "..",
82
- ".",
83
- ",",
84
- ":",
85
- "?"
86
- ],
87
- comparator: [
88
- ">=",
89
- "<=",
90
- "<",
91
- ">",
92
- "==",
93
- "!="
94
- ],
95
- keyword: ["$", "@"],
96
- boolean: ["true", "false"],
97
- paren: ["[", "]"]
70
+ // NOTE: These are compared against in order of definition,
71
+ // thus '==' must come before '=', '>=' before '>', etc.
72
+ operator: ["..", ".", ",", ":", "?"],
73
+ comparator: [">=", "<=", "<", ">", "==", "!="],
74
+ keyword: ["$", "@"],
75
+ boolean: ["true", "false"],
76
+ paren: ["[", "]"]
98
77
  }, symbolClasses = Object.keys(symbols);
99
- /**
100
- * Tokenizes a jsonpath2 expression
101
- */
102
- var Tokenizer = class {
103
- source;
104
- i;
105
- length;
106
- tokenizers;
107
- constructor(path) {
108
- this.source = path, this.length = path.length, this.i = 0, this.tokenizers = [
109
- this.tokenizeSymbol,
110
- this.tokenizeIdentifier,
111
- this.tokenizeNumber,
112
- this.tokenizeQuoted
113
- ].map((fn) => fn.bind(this));
114
- }
115
- tokenize() {
116
- let result = [];
117
- for (; !this.EOF();) {
118
- this.chompWhitespace();
119
- let token = null;
120
- if (!this.tokenizers.some((tokenizer) => (token = tokenizer(), !!token)) || !token) throw Error(`Invalid tokens in jsonpath '${this.source}' @ ${this.i}`);
121
- result.push(token);
122
- }
123
- return result;
124
- }
125
- takeWhile(fn) {
126
- let start = this.i, result = "";
127
- for (; !this.EOF();) {
128
- let nextChar = fn(this.source[this.i]);
129
- if (nextChar === null) break;
130
- result += nextChar, this.i++;
131
- }
132
- return this.i === start ? null : result;
133
- }
134
- EOF() {
135
- return this.i >= this.length;
136
- }
137
- peek() {
138
- return this.EOF() ? null : this.source[this.i];
139
- }
140
- consume(str) {
141
- if (this.i + str.length > this.length) throw Error(`Expected ${str} at end of jsonpath`);
142
- if (str === this.source.slice(this.i, this.i + str.length)) this.i += str.length;
143
- else throw Error(`Expected "${str}", but source contained "${this.source.slice()}`);
144
- }
145
- tryConsume(str) {
146
- if (this.i + str.length > this.length) return null;
147
- if (str === this.source.slice(this.i, this.i + str.length)) {
148
- if (str[0].match(attributeCharMatcher) && this.length > this.i + str.length) {
149
- let nextChar = this.source[this.i + str.length];
150
- if (nextChar && nextChar.match(attributeCharMatcher)) return null;
151
- }
152
- return this.i += str.length, str;
153
- }
154
- return null;
155
- }
156
- chompWhitespace() {
157
- this.takeWhile((char) => char === " " ? "" : null);
158
- }
159
- tokenizeQuoted() {
160
- let quote = this.peek();
161
- if (quote === "'" || quote === "\"") {
162
- this.consume(quote);
163
- let escape = !1, inner = this.takeWhile((char) => escape ? (escape = !1, char) : char === "\\" ? (escape = !0, "") : char == quote ? null : char);
164
- return this.consume(quote), {
165
- type: "quoted",
166
- value: inner,
167
- quote: quote === "\"" ? "double" : "single"
168
- };
169
- }
170
- return null;
171
- }
172
- tokenizeIdentifier() {
173
- let first = !0, identifier = this.takeWhile((char) => first ? (first = !1, char.match(attributeFirstCharMatcher) ? char : null) : char.match(attributeCharMatcher) ? char : null);
174
- return identifier === null ? null : {
175
- type: "identifier",
176
- name: identifier
177
- };
178
- }
179
- tokenizeNumber() {
180
- let start = this.i, dotSeen = !1, digitSeen = !1, negative = !1;
181
- this.peek() === "-" && (negative = !0, this.consume("-"));
182
- let number = this.takeWhile((char) => char === "." && !dotSeen && digitSeen ? (dotSeen = !0, char) : (digitSeen = !0, char.match(digitChar) ? char : null));
183
- return number === null ? (this.i = start, null) : {
184
- type: "number",
185
- value: negative ? -Number(number) : +number,
186
- raw: negative ? `-${number}` : number
187
- };
188
- }
189
- tokenizeSymbol() {
190
- for (let symbolClass of symbolClasses) {
191
- let symbol = symbols[symbolClass].find((pattern) => this.tryConsume(pattern));
192
- if (symbol) return {
193
- type: symbolClass,
194
- symbol
195
- };
196
- }
197
- return null;
198
- }
199
- };
78
+ class Tokenizer {
79
+ source;
80
+ i;
81
+ length;
82
+ tokenizers;
83
+ constructor(path) {
84
+ this.source = path, this.length = path.length, this.i = 0, this.tokenizers = [
85
+ this.tokenizeSymbol,
86
+ this.tokenizeIdentifier,
87
+ this.tokenizeNumber,
88
+ this.tokenizeQuoted
89
+ ].map((fn) => fn.bind(this));
90
+ }
91
+ tokenize() {
92
+ const result = [];
93
+ for (; !this.EOF(); ) {
94
+ this.chompWhitespace();
95
+ let token = null;
96
+ if (!this.tokenizers.some((tokenizer) => (token = tokenizer(), !!token)) || !token)
97
+ throw new Error(`Invalid tokens in jsonpath '${this.source}' @ ${this.i}`);
98
+ result.push(token);
99
+ }
100
+ return result;
101
+ }
102
+ takeWhile(fn) {
103
+ const start = this.i;
104
+ let result = "";
105
+ for (; !this.EOF(); ) {
106
+ const nextChar = fn(this.source[this.i]);
107
+ if (nextChar === null)
108
+ break;
109
+ result += nextChar, this.i++;
110
+ }
111
+ return this.i === start ? null : result;
112
+ }
113
+ EOF() {
114
+ return this.i >= this.length;
115
+ }
116
+ peek() {
117
+ return this.EOF() ? null : this.source[this.i];
118
+ }
119
+ consume(str) {
120
+ if (this.i + str.length > this.length)
121
+ throw new Error(`Expected ${str} at end of jsonpath`);
122
+ if (str === this.source.slice(this.i, this.i + str.length))
123
+ this.i += str.length;
124
+ else
125
+ throw new Error(`Expected "${str}", but source contained "${this.source.slice()}`);
126
+ }
127
+ // Tries to match the upcoming bit of string with the provided string. If it matches, returns
128
+ // the string, then advances the read pointer to the next bit. If not, returns null and nothing
129
+ // happens.
130
+ tryConsume(str) {
131
+ if (this.i + str.length > this.length)
132
+ return null;
133
+ if (str === this.source.slice(this.i, this.i + str.length)) {
134
+ if (str[0].match(attributeCharMatcher) && this.length > this.i + str.length) {
135
+ const nextChar = this.source[this.i + str.length];
136
+ if (nextChar && nextChar.match(attributeCharMatcher))
137
+ return null;
138
+ }
139
+ return this.i += str.length, str;
140
+ }
141
+ return null;
142
+ }
143
+ chompWhitespace() {
144
+ this.takeWhile((char) => char === " " ? "" : null);
145
+ }
146
+ tokenizeQuoted() {
147
+ const quote = this.peek();
148
+ if (quote === "'" || quote === '"') {
149
+ this.consume(quote);
150
+ let escape = !1;
151
+ const inner = this.takeWhile((char) => escape ? (escape = !1, char) : char === "\\" ? (escape = !0, "") : char != quote ? char : null);
152
+ return this.consume(quote), {
153
+ type: "quoted",
154
+ value: inner,
155
+ quote: quote === '"' ? "double" : "single"
156
+ };
157
+ }
158
+ return null;
159
+ }
160
+ tokenizeIdentifier() {
161
+ let first = !0;
162
+ const identifier = this.takeWhile((char) => first ? (first = !1, char.match(attributeFirstCharMatcher) ? char : null) : char.match(attributeCharMatcher) ? char : null);
163
+ return identifier !== null ? {
164
+ type: "identifier",
165
+ name: identifier
166
+ } : null;
167
+ }
168
+ tokenizeNumber() {
169
+ const start = this.i;
170
+ let dotSeen = !1, digitSeen = !1, negative = !1;
171
+ this.peek() === "-" && (negative = !0, this.consume("-"));
172
+ const number = this.takeWhile((char) => char === "." && !dotSeen && digitSeen ? (dotSeen = !0, char) : (digitSeen = !0, char.match(digitChar) ? char : null));
173
+ return number !== null ? {
174
+ type: "number",
175
+ value: negative ? -Number(number) : +number,
176
+ raw: negative ? `-${number}` : number
177
+ } : (this.i = start, null);
178
+ }
179
+ tokenizeSymbol() {
180
+ for (const symbolClass of symbolClasses) {
181
+ const symbol = symbols[symbolClass].find((pattern) => this.tryConsume(pattern));
182
+ if (symbol)
183
+ return {
184
+ type: symbolClass,
185
+ symbol
186
+ };
187
+ }
188
+ return null;
189
+ }
190
+ }
200
191
  function tokenize(jsonpath) {
201
- return new Tokenizer(jsonpath).tokenize();
192
+ return new Tokenizer(jsonpath).tokenize();
193
+ }
194
+ class Parser {
195
+ tokens;
196
+ length;
197
+ i;
198
+ constructor(path) {
199
+ this.tokens = tokenize(path), this.length = this.tokens.length, this.i = 0;
200
+ }
201
+ parse() {
202
+ return this.parsePath();
203
+ }
204
+ EOF() {
205
+ return this.i >= this.length;
206
+ }
207
+ // Look at upcoming token
208
+ peek() {
209
+ return this.EOF() ? null : this.tokens[this.i];
210
+ }
211
+ consume() {
212
+ const result = this.peek();
213
+ return this.i += 1, result;
214
+ }
215
+ // Return next token if it matches the pattern
216
+ probe(pattern) {
217
+ const token = this.peek();
218
+ if (!token)
219
+ return null;
220
+ const record = token;
221
+ return Object.keys(pattern).every((key) => key in token && pattern[key] === record[key]) ? token : null;
222
+ }
223
+ // Return and consume next token if it matches the pattern
224
+ match(pattern) {
225
+ return this.probe(pattern) ? this.consume() : null;
226
+ }
227
+ parseAttribute() {
228
+ const token = this.match({ type: "identifier" });
229
+ if (token && token.type === "identifier")
230
+ return {
231
+ type: "attribute",
232
+ name: token.name
233
+ };
234
+ const quoted = this.match({ type: "quoted", quote: "single" });
235
+ return quoted && quoted.type === "quoted" ? {
236
+ type: "attribute",
237
+ name: quoted.value || ""
238
+ } : null;
239
+ }
240
+ parseAlias() {
241
+ return this.match({ type: "keyword", symbol: "@" }) || this.match({ type: "keyword", symbol: "$" }) ? {
242
+ type: "alias",
243
+ target: "self"
244
+ } : null;
245
+ }
246
+ parseNumber() {
247
+ const token = this.match({ type: "number" });
248
+ return token && token.type === "number" ? {
249
+ type: "number",
250
+ value: token.value
251
+ } : null;
252
+ }
253
+ parseNumberValue() {
254
+ const expr = this.parseNumber();
255
+ return expr ? expr.value : null;
256
+ }
257
+ parseSliceSelector() {
258
+ const start = this.i, rangeStart = this.parseNumberValue();
259
+ if (!this.match({ type: "operator", symbol: ":" }))
260
+ return rangeStart === null ? (this.i = start, null) : { type: "index", value: rangeStart };
261
+ const result = {
262
+ type: "range",
263
+ start: rangeStart,
264
+ end: this.parseNumberValue()
265
+ };
266
+ return this.match({ type: "operator", symbol: ":" }) && (result.step = this.parseNumberValue()), result.start === null && result.end === null ? (this.i = start, null) : result;
267
+ }
268
+ parseValueReference() {
269
+ return this.parseAttribute() || this.parseSliceSelector();
270
+ }
271
+ parseLiteralValue() {
272
+ const literalString = this.match({ type: "quoted", quote: "double" });
273
+ if (literalString && literalString.type === "quoted")
274
+ return {
275
+ type: "string",
276
+ value: literalString.value || ""
277
+ };
278
+ const literalBoolean = this.match({ type: "boolean" });
279
+ return literalBoolean && literalBoolean.type === "boolean" ? {
280
+ type: "boolean",
281
+ value: literalBoolean.symbol === "true"
282
+ } : this.parseNumber();
283
+ }
284
+ // The LHS of a filter constraint: a single attribute or a dotted chain
285
+ // (`asset._ref`). Only `.attribute` continuations are accepted; a `[union]` or
286
+ // `..recursive` segment would make the rewind in `parseFilterExpression()`
287
+ // ambiguous.
288
+ parseAttributePath() {
289
+ const first = this.parseAttribute();
290
+ if (!first)
291
+ return null;
292
+ const nodes = [first];
293
+ for (; this.match({ type: "operator", symbol: "." }); ) {
294
+ const next = this.parseAttribute();
295
+ if (!next)
296
+ throw new Error("Expected attribute name following '.'");
297
+ nodes.push(next);
298
+ }
299
+ return nodes.length === 1 ? nodes[0] : {
300
+ type: "path",
301
+ nodes
302
+ };
303
+ }
304
+ // TODO: Reorder constraints so that literal value is always on rhs, and variable is always
305
+ // on lhs.
306
+ parseFilterExpression() {
307
+ const start = this.i, expr = this.parseAttributePath() || this.parseAlias();
308
+ if (!expr)
309
+ return null;
310
+ if (this.match({ type: "operator", symbol: "?" }))
311
+ return {
312
+ type: "constraint",
313
+ operator: "?",
314
+ lhs: expr
315
+ };
316
+ const binOp = this.match({ type: "comparator" });
317
+ if (!binOp || binOp.type !== "comparator")
318
+ return this.i = start, null;
319
+ const lhs = expr, rhs = this.parseLiteralValue();
320
+ if (!rhs)
321
+ throw new Error(`Operator ${binOp.symbol} needs a literal value at the right hand side`);
322
+ return {
323
+ type: "constraint",
324
+ operator: binOp.symbol,
325
+ lhs,
326
+ rhs
327
+ };
328
+ }
329
+ parseExpression() {
330
+ return this.parseFilterExpression() || this.parseValueReference();
331
+ }
332
+ parseUnion() {
333
+ if (!this.match({ type: "paren", symbol: "[" }))
334
+ return null;
335
+ const terms = [];
336
+ let expr = this.parseFilterExpression() || this.parsePath() || this.parseValueReference();
337
+ for (; expr && (terms.push(expr), !this.match({ type: "paren", symbol: "]" })); ) {
338
+ if (!this.match({ type: "operator", symbol: "," }))
339
+ throw new Error("Expected ]");
340
+ if (expr = this.parseFilterExpression() || this.parsePath() || this.parseValueReference(), !expr)
341
+ throw new Error("Expected expression following ','");
342
+ }
343
+ return {
344
+ type: "union",
345
+ nodes: terms
346
+ };
347
+ }
348
+ parseRecursive() {
349
+ if (!this.match({ type: "operator", symbol: ".." }))
350
+ return null;
351
+ const subpath = this.parsePath();
352
+ if (!subpath)
353
+ throw new Error("Expected path following '..' operator");
354
+ return {
355
+ type: "recursive",
356
+ term: subpath
357
+ };
358
+ }
359
+ parsePath() {
360
+ const nodes = [], expr = this.parseAttribute() || this.parseUnion() || this.parseRecursive();
361
+ if (!expr)
362
+ return null;
363
+ for (nodes.push(expr); !this.EOF(); )
364
+ if (this.match({ type: "operator", symbol: "." })) {
365
+ const attr = this.parseAttribute();
366
+ if (!attr)
367
+ throw new Error("Expected attribute name following '.");
368
+ nodes.push(attr);
369
+ continue;
370
+ } else if (this.probe({ type: "paren", symbol: "[" })) {
371
+ const union = this.parseUnion();
372
+ if (!union)
373
+ throw new Error("Expected union following '['");
374
+ nodes.push(union);
375
+ } else {
376
+ const recursive = this.parseRecursive();
377
+ recursive && nodes.push(recursive);
378
+ break;
379
+ }
380
+ return nodes.length === 1 ? nodes[0] : {
381
+ type: "path",
382
+ nodes
383
+ };
384
+ }
202
385
  }
203
- var Parser = class {
204
- tokens;
205
- length;
206
- i;
207
- constructor(path) {
208
- this.tokens = tokenize(path), this.length = this.tokens.length, this.i = 0;
209
- }
210
- parse() {
211
- return this.parsePath();
212
- }
213
- EOF() {
214
- return this.i >= this.length;
215
- }
216
- peek() {
217
- return this.EOF() ? null : this.tokens[this.i];
218
- }
219
- consume() {
220
- let result = this.peek();
221
- return this.i += 1, result;
222
- }
223
- probe(pattern) {
224
- let token = this.peek();
225
- if (!token) return null;
226
- let record = token;
227
- return Object.keys(pattern).every((key) => key in token && pattern[key] === record[key]) ? token : null;
228
- }
229
- match(pattern) {
230
- return this.probe(pattern) ? this.consume() : null;
231
- }
232
- parseAttribute() {
233
- let token = this.match({ type: "identifier" });
234
- if (token && token.type === "identifier") return {
235
- type: "attribute",
236
- name: token.name
237
- };
238
- let quoted = this.match({
239
- type: "quoted",
240
- quote: "single"
241
- });
242
- return quoted && quoted.type === "quoted" ? {
243
- type: "attribute",
244
- name: quoted.value || ""
245
- } : null;
246
- }
247
- parseAlias() {
248
- return this.match({
249
- type: "keyword",
250
- symbol: "@"
251
- }) || this.match({
252
- type: "keyword",
253
- symbol: "$"
254
- }) ? {
255
- type: "alias",
256
- target: "self"
257
- } : null;
258
- }
259
- parseNumber() {
260
- let token = this.match({ type: "number" });
261
- return token && token.type === "number" ? {
262
- type: "number",
263
- value: token.value
264
- } : null;
265
- }
266
- parseNumberValue() {
267
- let expr = this.parseNumber();
268
- return expr ? expr.value : null;
269
- }
270
- parseSliceSelector() {
271
- let start = this.i, rangeStart = this.parseNumberValue();
272
- if (!this.match({
273
- type: "operator",
274
- symbol: ":"
275
- })) return rangeStart === null ? (this.i = start, null) : {
276
- type: "index",
277
- value: rangeStart
278
- };
279
- let result = {
280
- type: "range",
281
- start: rangeStart,
282
- end: this.parseNumberValue()
283
- };
284
- return this.match({
285
- type: "operator",
286
- symbol: ":"
287
- }) && (result.step = this.parseNumberValue()), result.start === null && result.end === null ? (this.i = start, null) : result;
288
- }
289
- parseValueReference() {
290
- return this.parseAttribute() || this.parseSliceSelector();
291
- }
292
- parseLiteralValue() {
293
- let literalString = this.match({
294
- type: "quoted",
295
- quote: "double"
296
- });
297
- if (literalString && literalString.type === "quoted") return {
298
- type: "string",
299
- value: literalString.value || ""
300
- };
301
- let literalBoolean = this.match({ type: "boolean" });
302
- return literalBoolean && literalBoolean.type === "boolean" ? {
303
- type: "boolean",
304
- value: literalBoolean.symbol === "true"
305
- } : this.parseNumber();
306
- }
307
- parseAttributePath() {
308
- let first = this.parseAttribute();
309
- if (!first) return null;
310
- let nodes = [first];
311
- for (; this.match({
312
- type: "operator",
313
- symbol: "."
314
- });) {
315
- let next = this.parseAttribute();
316
- if (!next) throw Error("Expected attribute name following '.'");
317
- nodes.push(next);
318
- }
319
- return nodes.length === 1 ? nodes[0] : {
320
- type: "path",
321
- nodes
322
- };
323
- }
324
- parseFilterExpression() {
325
- let start = this.i, expr = this.parseAttributePath() || this.parseAlias();
326
- if (!expr) return null;
327
- if (this.match({
328
- type: "operator",
329
- symbol: "?"
330
- })) return {
331
- type: "constraint",
332
- operator: "?",
333
- lhs: expr
334
- };
335
- let binOp = this.match({ type: "comparator" });
336
- if (!binOp || binOp.type !== "comparator") return this.i = start, null;
337
- let lhs = expr, rhs = this.parseLiteralValue();
338
- if (!rhs) throw Error(`Operator ${binOp.symbol} needs a literal value at the right hand side`);
339
- return {
340
- type: "constraint",
341
- operator: binOp.symbol,
342
- lhs,
343
- rhs
344
- };
345
- }
346
- parseExpression() {
347
- return this.parseFilterExpression() || this.parseValueReference();
348
- }
349
- parseUnion() {
350
- if (!this.match({
351
- type: "paren",
352
- symbol: "["
353
- })) return null;
354
- let terms = [], expr = this.parseFilterExpression() || this.parsePath() || this.parseValueReference();
355
- for (; expr && (terms.push(expr), !this.match({
356
- type: "paren",
357
- symbol: "]"
358
- }));) {
359
- if (!this.match({
360
- type: "operator",
361
- symbol: ","
362
- })) throw Error("Expected ]");
363
- if (expr = this.parseFilterExpression() || this.parsePath() || this.parseValueReference(), !expr) throw Error("Expected expression following ','");
364
- }
365
- return {
366
- type: "union",
367
- nodes: terms
368
- };
369
- }
370
- parseRecursive() {
371
- if (!this.match({
372
- type: "operator",
373
- symbol: ".."
374
- })) return null;
375
- let subpath = this.parsePath();
376
- if (!subpath) throw Error("Expected path following '..' operator");
377
- return {
378
- type: "recursive",
379
- term: subpath
380
- };
381
- }
382
- parsePath() {
383
- let nodes = [], expr = this.parseAttribute() || this.parseUnion() || this.parseRecursive();
384
- if (!expr) return null;
385
- for (nodes.push(expr); !this.EOF();) if (this.match({
386
- type: "operator",
387
- symbol: "."
388
- })) {
389
- let attr = this.parseAttribute();
390
- if (!attr) throw Error("Expected attribute name following '.");
391
- nodes.push(attr);
392
- continue;
393
- } else if (this.probe({
394
- type: "paren",
395
- symbol: "["
396
- })) {
397
- let union = this.parseUnion();
398
- if (!union) throw Error("Expected union following '['");
399
- nodes.push(union);
400
- } else {
401
- let recursive = this.parseRecursive();
402
- recursive && nodes.push(recursive);
403
- break;
404
- }
405
- return nodes.length === 1 ? nodes[0] : {
406
- type: "path",
407
- nodes
408
- };
409
- }
410
- };
411
386
  function parseJsonPath(path) {
412
- let parsed = new Parser(path).parse();
413
- if (!parsed) throw Error(`Failed to parse JSON path "${path}"`);
414
- return parsed;
387
+ const parsed = new Parser(path).parse();
388
+ if (!parsed)
389
+ throw new Error(`Failed to parse JSON path "${path}"`);
390
+ return parsed;
415
391
  }
416
- /**
417
- * Converts a parsed expression back into jsonpath2, roughly -
418
- * mostly for use with tests.
419
- *
420
- * @param expr - Expression to convert to path
421
- * @returns a string representation of the path
422
- * @internal
423
- */
424
392
  function toPath(expr) {
425
- return toPathInner(expr, !1);
393
+ return toPathInner(expr, !1);
426
394
  }
427
395
  function toPathInner(expr, inUnion) {
428
- switch (expr.type) {
429
- case "attribute": return expr.name;
430
- case "alias": return expr.target === "self" ? "@" : "$";
431
- case "number": return `${expr.value}`;
432
- case "range": {
433
- let result = [];
434
- return inUnion || result.push("["), expr.start && result.push(`${expr.start}`), result.push(":"), expr.end && result.push(`${expr.end}`), expr.step && result.push(`:${expr.step}`), inUnion || result.push("]"), result.join("");
435
- }
436
- case "index": return inUnion ? `${expr.value}` : `[${expr.value}]`;
437
- case "constraint": {
438
- let rhs = expr.rhs ? ` ${toPathInner(expr.rhs, !1)}` : "", inner = `${toPathInner(expr.lhs, !1)} ${expr.operator}${rhs}`;
439
- return inUnion ? inner : `[${inner}]`;
440
- }
441
- case "string": return JSON.stringify(expr.value);
442
- case "path": {
443
- let result = [], nodes = expr.nodes.slice();
444
- for (; nodes.length > 0;) {
445
- let node = nodes.shift();
446
- node && result.push(toPath(node));
447
- let upcoming = nodes[0];
448
- upcoming && toPathInner(upcoming, !1)[0] !== "[" && result.push(".");
449
- }
450
- return result.join("");
451
- }
452
- case "union": return `[${expr.nodes.map((e) => toPathInner(e, !0)).join(",")}]`;
453
- default: throw Error(`Unknown node type ${expr.type}`);
454
- case "recursive": return `..${toPathInner(expr.term, !1)}`;
455
- }
396
+ switch (expr.type) {
397
+ case "attribute":
398
+ return expr.name;
399
+ case "alias":
400
+ return expr.target === "self" ? "@" : "$";
401
+ case "number":
402
+ return `${expr.value}`;
403
+ case "range": {
404
+ const result = [];
405
+ return inUnion || result.push("["), expr.start && result.push(`${expr.start}`), result.push(":"), expr.end && result.push(`${expr.end}`), expr.step && result.push(`:${expr.step}`), inUnion || result.push("]"), result.join("");
406
+ }
407
+ case "index":
408
+ return inUnion ? `${expr.value}` : `[${expr.value}]`;
409
+ case "constraint": {
410
+ const rhs = expr.rhs ? ` ${toPathInner(expr.rhs, !1)}` : "", inner = `${toPathInner(expr.lhs, !1)} ${expr.operator}${rhs}`;
411
+ return inUnion ? inner : `[${inner}]`;
412
+ }
413
+ case "string":
414
+ return JSON.stringify(expr.value);
415
+ case "path": {
416
+ const result = [], nodes = expr.nodes.slice();
417
+ for (; nodes.length > 0; ) {
418
+ const node = nodes.shift();
419
+ node && result.push(toPath(node));
420
+ const upcoming = nodes[0];
421
+ upcoming && toPathInner(upcoming, !1)[0] !== "[" && result.push(".");
422
+ }
423
+ return result.join("");
424
+ }
425
+ case "union":
426
+ return `[${expr.nodes.map((e) => toPathInner(e, !0)).join(",")}]`;
427
+ default:
428
+ throw new Error(`Unknown node type ${expr.type}`);
429
+ case "recursive":
430
+ return `..${toPathInner(expr.term, !1)}`;
431
+ }
432
+ }
433
+ class Expression {
434
+ expr;
435
+ constructor(expr) {
436
+ if (!expr)
437
+ throw new Error("Attempted to create Expression from null-value");
438
+ if ("expr" in expr ? this.expr = expr.expr : this.expr = expr, !("type" in this.expr))
439
+ throw new Error("Attempt to create Expression for expression with no type");
440
+ }
441
+ isPath() {
442
+ return this.expr.type === "path";
443
+ }
444
+ isUnion() {
445
+ return this.expr.type === "union";
446
+ }
447
+ isCollection() {
448
+ return this.isPath() || this.isUnion();
449
+ }
450
+ isConstraint() {
451
+ return this.expr.type === "constraint";
452
+ }
453
+ isRecursive() {
454
+ return this.expr.type === "recursive";
455
+ }
456
+ isExistenceConstraint() {
457
+ return this.expr.type === "constraint" && this.expr.operator === "?";
458
+ }
459
+ isIndex() {
460
+ return this.expr.type === "index";
461
+ }
462
+ isRange() {
463
+ return this.expr.type === "range";
464
+ }
465
+ expandRange(probe) {
466
+ const probeLength = () => {
467
+ if (!probe)
468
+ throw new Error("expandRange() required a probe that was not passed");
469
+ return probe.length();
470
+ };
471
+ let start = "start" in this.expr && this.expr.start || 0;
472
+ start = interpretNegativeIndex(start, probe);
473
+ let end = "end" in this.expr && this.expr.end || probeLength();
474
+ end = interpretNegativeIndex(end, probe);
475
+ const step = "step" in this.expr && this.expr.step || 1;
476
+ return { start, end, step };
477
+ }
478
+ isAttributeReference() {
479
+ return this.expr.type === "attribute";
480
+ }
481
+ // Is a range or index -> something referencing indexes
482
+ isIndexReference() {
483
+ return this.isIndex() || this.isRange();
484
+ }
485
+ name() {
486
+ return "name" in this.expr ? this.expr.name : "";
487
+ }
488
+ isSelfReference() {
489
+ return this.expr.type === "alias" && this.expr.target === "self";
490
+ }
491
+ constraintTargetIsSelf() {
492
+ return this.expr.type === "constraint" && this.expr.lhs.type === "alias" && this.expr.lhs.target === "self";
493
+ }
494
+ constraintTargetIsAttribute() {
495
+ return this.expr.type === "constraint" && this.expr.lhs.type === "attribute";
496
+ }
497
+ testConstraint(probe) {
498
+ const expr = this.expr;
499
+ if (expr.type === "constraint" && expr.lhs.type === "alias" && expr.lhs.target === "self") {
500
+ if (probe.containerType() !== "primitive")
501
+ return !1;
502
+ if (expr.type === "constraint" && expr.operator === "?")
503
+ return !0;
504
+ const lhs2 = probe.get(), rhs2 = expr.rhs && "value" in expr.rhs ? expr.rhs.value : void 0;
505
+ return testBinaryOperator(lhs2, expr.operator, rhs2);
506
+ }
507
+ if (expr.type !== "constraint")
508
+ return !1;
509
+ const lhs = expr.lhs;
510
+ if (!lhs)
511
+ throw new Error("No LHS of expression");
512
+ if (lhs.type !== "attribute" && lhs.type !== "path")
513
+ throw new Error(`Constraint target ${lhs.type} not supported`);
514
+ if (probe.containerType() !== "object")
515
+ return !1;
516
+ const attrNames = lhs.type === "attribute" ? [lhs.name] : lhs.nodes.map((node) => {
517
+ if (node.type !== "attribute")
518
+ throw new Error(
519
+ `Constraint target path with non-attribute segment '${node.type}' not supported`
520
+ );
521
+ return node.name;
522
+ });
523
+ let cursor = probe;
524
+ for (let i = 0; i < attrNames.length - 1; i++) {
525
+ if (cursor.containerType() !== "object")
526
+ return !1;
527
+ const next = cursor.getAttribute(attrNames[i]);
528
+ if (next === null)
529
+ return !1;
530
+ cursor = next;
531
+ }
532
+ if (cursor.containerType() !== "object")
533
+ return !1;
534
+ const lhsValue = cursor.getAttribute(attrNames[attrNames.length - 1]);
535
+ if (lhsValue == null || lhsValue.containerType() !== "primitive")
536
+ return !1;
537
+ if (this.isExistenceConstraint())
538
+ return !0;
539
+ const rhs = expr.rhs && "value" in expr.rhs ? expr.rhs.value : void 0;
540
+ return testBinaryOperator(lhsValue.get(), expr.operator, rhs);
541
+ }
542
+ pathNodes() {
543
+ return this.expr.type === "path" ? this.expr.nodes : [this.expr];
544
+ }
545
+ prepend(node) {
546
+ return node ? new Expression({
547
+ type: "path",
548
+ nodes: node.pathNodes().concat(this.pathNodes())
549
+ }) : this;
550
+ }
551
+ concat(other) {
552
+ return other ? other.prepend(this) : this;
553
+ }
554
+ descend() {
555
+ return descend$1(this.expr).map((headTail) => {
556
+ const [head, tail] = headTail;
557
+ return {
558
+ head: head ? new Expression(head) : null,
559
+ tail: tail ? new Expression(tail) : null
560
+ };
561
+ });
562
+ }
563
+ unwrapRecursive() {
564
+ if (this.expr.type !== "recursive")
565
+ throw new Error(`Attempt to unwrap recursive on type ${this.expr.type}`);
566
+ return new Expression(this.expr.term);
567
+ }
568
+ toIndicies(probe) {
569
+ if (this.expr.type !== "index" && this.expr.type !== "range")
570
+ throw new Error("Node cannot be converted to indexes");
571
+ if (this.expr.type === "index")
572
+ return [interpretNegativeIndex(this.expr.value, probe)];
573
+ const result = [], range = this.expandRange(probe);
574
+ let { start, end } = range;
575
+ range.step < 0 && ([start, end] = [end, start]);
576
+ for (let i = start; i < end; i++)
577
+ result.push(i);
578
+ return result;
579
+ }
580
+ toFieldReferences() {
581
+ if (this.isIndexReference())
582
+ return this.toIndicies();
583
+ if (this.expr.type === "attribute")
584
+ return [this.expr.name];
585
+ throw new Error(`Can't convert ${this.expr.type} to field references`);
586
+ }
587
+ toString() {
588
+ return toPath(this.expr);
589
+ }
590
+ static fromPath(path) {
591
+ const parsed = parseJsonPath(path);
592
+ if (!parsed)
593
+ throw new Error(`Failed to parse path "${path}"`);
594
+ return new Expression(parsed);
595
+ }
596
+ static attributeReference(name) {
597
+ return new Expression({
598
+ type: "attribute",
599
+ name
600
+ });
601
+ }
602
+ static indexReference(i) {
603
+ return new Expression({
604
+ type: "index",
605
+ value: i
606
+ });
607
+ }
456
608
  }
457
- var Expression = class Expression {
458
- expr;
459
- constructor(expr) {
460
- if (!expr) throw Error("Attempted to create Expression from null-value");
461
- if ("expr" in expr ? this.expr = expr.expr : this.expr = expr, !("type" in this.expr)) throw Error("Attempt to create Expression for expression with no type");
462
- }
463
- isPath() {
464
- return this.expr.type === "path";
465
- }
466
- isUnion() {
467
- return this.expr.type === "union";
468
- }
469
- isCollection() {
470
- return this.isPath() || this.isUnion();
471
- }
472
- isConstraint() {
473
- return this.expr.type === "constraint";
474
- }
475
- isRecursive() {
476
- return this.expr.type === "recursive";
477
- }
478
- isExistenceConstraint() {
479
- return this.expr.type === "constraint" && this.expr.operator === "?";
480
- }
481
- isIndex() {
482
- return this.expr.type === "index";
483
- }
484
- isRange() {
485
- return this.expr.type === "range";
486
- }
487
- expandRange(probe) {
488
- let probeLength = () => {
489
- if (!probe) throw Error("expandRange() required a probe that was not passed");
490
- return probe.length();
491
- }, start = "start" in this.expr && this.expr.start || 0;
492
- start = interpretNegativeIndex(start, probe);
493
- let end = "end" in this.expr && this.expr.end || probeLength();
494
- end = interpretNegativeIndex(end, probe);
495
- let step = "step" in this.expr && this.expr.step || 1;
496
- return {
497
- start,
498
- end,
499
- step
500
- };
501
- }
502
- isAttributeReference() {
503
- return this.expr.type === "attribute";
504
- }
505
- isIndexReference() {
506
- return this.isIndex() || this.isRange();
507
- }
508
- name() {
509
- return "name" in this.expr ? this.expr.name : "";
510
- }
511
- isSelfReference() {
512
- return this.expr.type === "alias" && this.expr.target === "self";
513
- }
514
- constraintTargetIsSelf() {
515
- return this.expr.type === "constraint" && this.expr.lhs.type === "alias" && this.expr.lhs.target === "self";
516
- }
517
- constraintTargetIsAttribute() {
518
- return this.expr.type === "constraint" && this.expr.lhs.type === "attribute";
519
- }
520
- testConstraint(probe) {
521
- let expr = this.expr;
522
- if (expr.type === "constraint" && expr.lhs.type === "alias" && expr.lhs.target === "self") {
523
- if (probe.containerType() !== "primitive") return !1;
524
- if (expr.type === "constraint" && expr.operator === "?") return !0;
525
- let lhs = probe.get(), rhs = expr.rhs && "value" in expr.rhs ? expr.rhs.value : void 0;
526
- return testBinaryOperator(lhs, expr.operator, rhs);
527
- }
528
- if (expr.type !== "constraint") return !1;
529
- let lhs = expr.lhs;
530
- if (!lhs) throw Error("No LHS of expression");
531
- if (lhs.type !== "attribute" && lhs.type !== "path") throw Error(`Constraint target ${lhs.type} not supported`);
532
- if (probe.containerType() !== "object") return !1;
533
- let attrNames = lhs.type === "attribute" ? [lhs.name] : lhs.nodes.map((node) => {
534
- if (node.type !== "attribute") throw Error(`Constraint target path with non-attribute segment '${node.type}' not supported`);
535
- return node.name;
536
- }), cursor = probe;
537
- for (let i = 0; i < attrNames.length - 1; i++) {
538
- if (cursor.containerType() !== "object") return !1;
539
- let next = cursor.getAttribute(attrNames[i]);
540
- if (next === null) return !1;
541
- cursor = next;
542
- }
543
- if (cursor.containerType() !== "object") return !1;
544
- let lhsValue = cursor.getAttribute(attrNames[attrNames.length - 1]);
545
- if (lhsValue == null || lhsValue.containerType() !== "primitive") return !1;
546
- if (this.isExistenceConstraint()) return !0;
547
- let rhs = expr.rhs && "value" in expr.rhs ? expr.rhs.value : void 0;
548
- return testBinaryOperator(lhsValue.get(), expr.operator, rhs);
549
- }
550
- pathNodes() {
551
- return this.expr.type === "path" ? this.expr.nodes : [this.expr];
552
- }
553
- prepend(node) {
554
- return node ? new Expression({
555
- type: "path",
556
- nodes: node.pathNodes().concat(this.pathNodes())
557
- }) : this;
558
- }
559
- concat(other) {
560
- return other ? other.prepend(this) : this;
561
- }
562
- descend() {
563
- return descend$1(this.expr).map((headTail) => {
564
- let [head, tail] = headTail;
565
- return {
566
- head: head ? new Expression(head) : null,
567
- tail: tail ? new Expression(tail) : null
568
- };
569
- });
570
- }
571
- unwrapRecursive() {
572
- if (this.expr.type !== "recursive") throw Error(`Attempt to unwrap recursive on type ${this.expr.type}`);
573
- return new Expression(this.expr.term);
574
- }
575
- toIndicies(probe) {
576
- if (this.expr.type !== "index" && this.expr.type !== "range") throw Error("Node cannot be converted to indexes");
577
- if (this.expr.type === "index") return [interpretNegativeIndex(this.expr.value, probe)];
578
- let result = [], range = this.expandRange(probe), { start, end } = range;
579
- range.step < 0 && ([start, end] = [end, start]);
580
- for (let i = start; i < end; i++) result.push(i);
581
- return result;
582
- }
583
- toFieldReferences() {
584
- if (this.isIndexReference()) return this.toIndicies();
585
- if (this.expr.type === "attribute") return [this.expr.name];
586
- throw Error(`Can't convert ${this.expr.type} to field references`);
587
- }
588
- toString() {
589
- return toPath(this.expr);
590
- }
591
- static fromPath(path) {
592
- let parsed = parseJsonPath(path);
593
- if (!parsed) throw Error(`Failed to parse path "${path}"`);
594
- return new Expression(parsed);
595
- }
596
- static attributeReference(name) {
597
- return new Expression({
598
- type: "attribute",
599
- name
600
- });
601
- }
602
- static indexReference(i) {
603
- return new Expression({
604
- type: "index",
605
- value: i
606
- });
607
- }
608
- };
609
609
  function testBinaryOperator(lhsValue, operator, rhsValue) {
610
- switch (operator) {
611
- case ">": return lhsValue > rhsValue;
612
- case ">=": return lhsValue >= rhsValue;
613
- case "<": return lhsValue < rhsValue;
614
- case "<=": return lhsValue <= rhsValue;
615
- case "==": return lhsValue === rhsValue;
616
- case "!=": return lhsValue !== rhsValue;
617
- default: throw Error(`Unsupported binary operator ${operator}`);
618
- }
610
+ switch (operator) {
611
+ case ">":
612
+ return lhsValue > rhsValue;
613
+ case ">=":
614
+ return lhsValue >= rhsValue;
615
+ case "<":
616
+ return lhsValue < rhsValue;
617
+ case "<=":
618
+ return lhsValue <= rhsValue;
619
+ case "==":
620
+ return lhsValue === rhsValue;
621
+ case "!=":
622
+ return lhsValue !== rhsValue;
623
+ default:
624
+ throw new Error(`Unsupported binary operator ${operator}`);
625
+ }
619
626
  }
620
627
  function interpretNegativeIndex(index, probe) {
621
- if (index >= 0) return index;
622
- if (!probe) throw Error("interpretNegativeIndex() must have a probe when < 0");
623
- return index + probe.length();
628
+ if (index >= 0)
629
+ return index;
630
+ if (!probe)
631
+ throw new Error("interpretNegativeIndex() must have a probe when < 0");
632
+ return index + probe.length();
633
+ }
634
+ class Descender {
635
+ head;
636
+ tail;
637
+ constructor(head, tail) {
638
+ this.head = head, this.tail = tail;
639
+ }
640
+ // Iterate this descender once processing any constraints that are
641
+ // resolvable on the current value. Returns an array of new descenders
642
+ // that are guaranteed to be without constraints in the head
643
+ iterate(probe) {
644
+ let result = [this];
645
+ if (this.head && this.head.isConstraint()) {
646
+ let anyConstraints = !0;
647
+ for (; anyConstraints; )
648
+ result = flatten(
649
+ result.map((descender) => descender.iterateConstraints(probe))
650
+ ), anyConstraints = result.some((descender) => descender.head && descender.head.isConstraint());
651
+ }
652
+ return result;
653
+ }
654
+ isRecursive() {
655
+ return !!(this.head && this.head.isRecursive());
656
+ }
657
+ hasArrived() {
658
+ return this.head === null && this.tail === null;
659
+ }
660
+ extractRecursives() {
661
+ if (this.head && this.head.isRecursive()) {
662
+ const term = this.head.unwrapRecursive();
663
+ return new Descender(null, term.concat(this.tail)).descend();
664
+ }
665
+ return [];
666
+ }
667
+ iterateConstraints(probe) {
668
+ const head = this.head;
669
+ if (head === null || !head.isConstraint())
670
+ return [this];
671
+ const result = [];
672
+ if (probe.containerType() === "primitive" && head.constraintTargetIsSelf())
673
+ return head.testConstraint(probe) && result.push(...this.descend()), result;
674
+ if (probe.containerType() === "array") {
675
+ const length = probe.length();
676
+ for (let i = 0; i < length; i++) {
677
+ const constraint = probe.getIndex(i);
678
+ constraint && head.testConstraint(constraint) && result.push(new Descender(new Expression({ type: "index", value: i }), this.tail));
679
+ }
680
+ return result;
681
+ }
682
+ return probe.containerType() === "object" ? head.constraintTargetIsSelf() ? [] : head.testConstraint(probe) ? this.descend() : result : result;
683
+ }
684
+ descend() {
685
+ return this.tail ? this.tail.descend().map((ht) => new Descender(ht.head, ht.tail)) : [new Descender(null, null)];
686
+ }
687
+ toString() {
688
+ const result = ["<"];
689
+ return this.head && result.push(this.head.toString()), result.push("|"), this.tail && result.push(this.tail.toString()), result.push(">"), result.join("");
690
+ }
691
+ }
692
+ class Matcher {
693
+ active;
694
+ recursives;
695
+ payload;
696
+ constructor(active, parent) {
697
+ this.active = active || [], parent ? (this.recursives = parent.recursives, this.payload = parent.payload) : this.recursives = [], this.extractRecursives();
698
+ }
699
+ setPayload(payload) {
700
+ return this.payload = payload, this;
701
+ }
702
+ // Moves any recursive descenders onto the recursive track, removing them from
703
+ // the active set
704
+ extractRecursives() {
705
+ this.active = this.active.filter((descender) => descender.isRecursive() ? (this.recursives.push(...descender.extractRecursives()), !1) : !0);
706
+ }
707
+ // Find recursives that are relevant now and should be considered part of the active set
708
+ activeRecursives(probe) {
709
+ return this.recursives.filter((descender) => {
710
+ const head = descender.head;
711
+ return head ? head.isConstraint() || probe.containerType() === "array" && head.isIndexReference() ? !0 : probe.containerType() === "object" ? head.isAttributeReference() && probe.hasAttribute(head.name()) : !1 : !1;
712
+ });
713
+ }
714
+ match(probe) {
715
+ return this.iterate(probe).extractMatches(probe);
716
+ }
717
+ iterate(probe) {
718
+ const newActiveSet = [];
719
+ return this.active.concat(this.activeRecursives(probe)).forEach((descender) => {
720
+ newActiveSet.push(...descender.iterate(probe));
721
+ }), new Matcher(newActiveSet, this);
722
+ }
723
+ // Returns true if any of the descenders in the active or recursive set
724
+ // consider the current state a final destination
725
+ isDestination() {
726
+ return this.active.some((descender) => descender.hasArrived());
727
+ }
728
+ hasRecursives() {
729
+ return this.recursives.length > 0;
730
+ }
731
+ // Returns any payload delivieries and leads that needs to be followed to complete
732
+ // the process.
733
+ extractMatches(probe) {
734
+ const leads = [], targets = [];
735
+ if (this.active.forEach((descender) => {
736
+ if (descender.hasArrived()) {
737
+ targets.push(
738
+ new Expression({
739
+ type: "alias",
740
+ target: "self"
741
+ })
742
+ );
743
+ return;
744
+ }
745
+ const descenderHead = descender.head;
746
+ if (descenderHead && !(probe.containerType() === "array" && !descenderHead.isIndexReference()) && !(probe.containerType() === "object" && !descenderHead.isAttributeReference()))
747
+ if (descender.tail) {
748
+ const matcher = new Matcher(descender.descend(), this);
749
+ descenderHead.toFieldReferences().forEach(() => {
750
+ leads.push({
751
+ target: descenderHead,
752
+ matcher
753
+ });
754
+ });
755
+ } else
756
+ targets.push(descenderHead);
757
+ }), this.hasRecursives()) {
758
+ const recursivesMatcher = new Matcher([], this);
759
+ if (probe.containerType() === "array") {
760
+ const length = probe.length();
761
+ for (let i = 0; i < length; i++)
762
+ leads.push({
763
+ target: Expression.indexReference(i),
764
+ matcher: recursivesMatcher
765
+ });
766
+ } else probe.containerType() === "object" && probe.attributeKeys().forEach((name) => {
767
+ leads.push({
768
+ target: Expression.attributeReference(name),
769
+ matcher: recursivesMatcher
770
+ });
771
+ });
772
+ }
773
+ return targets.length > 0 ? { leads, delivery: { targets, payload: this.payload } } : { leads };
774
+ }
775
+ static fromPath(jsonpath) {
776
+ const path = parseJsonPath(jsonpath);
777
+ if (!path)
778
+ throw new Error(`Failed to parse path from "${jsonpath}"`);
779
+ const descender = new Descender(null, new Expression(path));
780
+ return new Matcher(descender.descend());
781
+ }
782
+ }
783
+ class PlainProbe {
784
+ _value;
785
+ path;
786
+ constructor(value, path) {
787
+ this._value = value, this.path = path || [];
788
+ }
789
+ containerType() {
790
+ return Array.isArray(this._value) ? "array" : this._value !== null && typeof this._value == "object" ? "object" : "primitive";
791
+ }
792
+ length() {
793
+ if (!Array.isArray(this._value))
794
+ throw new Error("Won't return length of non-indexable _value");
795
+ return this._value.length;
796
+ }
797
+ getIndex(i) {
798
+ return Array.isArray(this._value) ? i >= this.length() ? null : new PlainProbe(this._value[i], this.path.concat(i)) : !1;
799
+ }
800
+ hasAttribute(key) {
801
+ return isRecord$1(this._value) ? this._value.hasOwnProperty(key) : !1;
802
+ }
803
+ attributeKeys() {
804
+ return isRecord$1(this._value) ? Object.keys(this._value) : [];
805
+ }
806
+ getAttribute(key) {
807
+ if (!isRecord$1(this._value))
808
+ throw new Error("getAttribute only applies to plain objects");
809
+ return this.hasAttribute(key) ? new PlainProbe(this._value[key], this.path.concat(key)) : null;
810
+ }
811
+ get() {
812
+ return this._value;
813
+ }
624
814
  }
625
- /**
626
- * Descender models the state of one partial jsonpath evaluation. Head is the
627
- * next thing to match, tail is the upcoming things once the head is matched.
628
- */
629
- var Descender = class Descender {
630
- head;
631
- tail;
632
- constructor(head, tail) {
633
- this.head = head, this.tail = tail;
634
- }
635
- iterate(probe) {
636
- let result = [this];
637
- if (this.head && this.head.isConstraint()) {
638
- let anyConstraints = !0;
639
- for (; anyConstraints;) result = flatten(result.map((descender) => descender.iterateConstraints(probe))), anyConstraints = result.some((descender) => descender.head && descender.head.isConstraint());
640
- }
641
- return result;
642
- }
643
- isRecursive() {
644
- return !!(this.head && this.head.isRecursive());
645
- }
646
- hasArrived() {
647
- return this.head === null && this.tail === null;
648
- }
649
- extractRecursives() {
650
- if (this.head && this.head.isRecursive()) {
651
- let term = this.head.unwrapRecursive();
652
- return new Descender(null, term.concat(this.tail)).descend();
653
- }
654
- return [];
655
- }
656
- iterateConstraints(probe) {
657
- let head = this.head;
658
- if (head === null || !head.isConstraint()) return [this];
659
- let result = [];
660
- if (probe.containerType() === "primitive" && head.constraintTargetIsSelf()) return head.testConstraint(probe) && result.push(...this.descend()), result;
661
- if (probe.containerType() === "array") {
662
- let length = probe.length();
663
- for (let i = 0; i < length; i++) {
664
- let constraint = probe.getIndex(i);
665
- constraint && head.testConstraint(constraint) && result.push(new Descender(new Expression({
666
- type: "index",
667
- value: i
668
- }), this.tail));
669
- }
670
- return result;
671
- }
672
- return probe.containerType() === "object" ? head.constraintTargetIsSelf() ? [] : head.testConstraint(probe) ? this.descend() : result : result;
673
- }
674
- descend() {
675
- return this.tail ? this.tail.descend().map((ht) => new Descender(ht.head, ht.tail)) : [new Descender(null, null)];
676
- }
677
- toString() {
678
- let result = ["<"];
679
- return this.head && result.push(this.head.toString()), result.push("|"), this.tail && result.push(this.tail.toString()), result.push(">"), result.join("");
680
- }
681
- }, Matcher = class Matcher {
682
- active;
683
- recursives;
684
- payload;
685
- constructor(active, parent) {
686
- this.active = active || [], parent ? (this.recursives = parent.recursives, this.payload = parent.payload) : this.recursives = [], this.extractRecursives();
687
- }
688
- setPayload(payload) {
689
- return this.payload = payload, this;
690
- }
691
- extractRecursives() {
692
- this.active = this.active.filter((descender) => descender.isRecursive() ? (this.recursives.push(...descender.extractRecursives()), !1) : !0);
693
- }
694
- activeRecursives(probe) {
695
- return this.recursives.filter((descender) => {
696
- let head = descender.head;
697
- return head ? head.isConstraint() || probe.containerType() === "array" && head.isIndexReference() ? !0 : probe.containerType() === "object" && head.isAttributeReference() && probe.hasAttribute(head.name()) : !1;
698
- });
699
- }
700
- match(probe) {
701
- return this.iterate(probe).extractMatches(probe);
702
- }
703
- iterate(probe) {
704
- let newActiveSet = [];
705
- return this.active.concat(this.activeRecursives(probe)).forEach((descender) => {
706
- newActiveSet.push(...descender.iterate(probe));
707
- }), new Matcher(newActiveSet, this);
708
- }
709
- isDestination() {
710
- return this.active.some((descender) => descender.hasArrived());
711
- }
712
- hasRecursives() {
713
- return this.recursives.length > 0;
714
- }
715
- extractMatches(probe) {
716
- let leads = [], targets = [];
717
- if (this.active.forEach((descender) => {
718
- if (descender.hasArrived()) {
719
- targets.push(new Expression({
720
- type: "alias",
721
- target: "self"
722
- }));
723
- return;
724
- }
725
- let descenderHead = descender.head;
726
- if (descenderHead && !(probe.containerType() === "array" && !descenderHead.isIndexReference()) && !(probe.containerType() === "object" && !descenderHead.isAttributeReference())) if (descender.tail) {
727
- let matcher = new Matcher(descender.descend(), this);
728
- descenderHead.toFieldReferences().forEach(() => {
729
- leads.push({
730
- target: descenderHead,
731
- matcher
732
- });
733
- });
734
- } else targets.push(descenderHead);
735
- }), this.hasRecursives()) {
736
- let recursivesMatcher = new Matcher([], this);
737
- if (probe.containerType() === "array") {
738
- let length = probe.length();
739
- for (let i = 0; i < length; i++) leads.push({
740
- target: Expression.indexReference(i),
741
- matcher: recursivesMatcher
742
- });
743
- } else probe.containerType() === "object" && probe.attributeKeys().forEach((name) => {
744
- leads.push({
745
- target: Expression.attributeReference(name),
746
- matcher: recursivesMatcher
747
- });
748
- });
749
- }
750
- return targets.length > 0 ? {
751
- leads,
752
- delivery: {
753
- targets,
754
- payload: this.payload
755
- }
756
- } : { leads };
757
- }
758
- static fromPath(jsonpath) {
759
- let path = parseJsonPath(jsonpath);
760
- if (!path) throw Error(`Failed to parse path from "${jsonpath}"`);
761
- let descender = new Descender(null, new Expression(path));
762
- return new Matcher(descender.descend());
763
- }
764
- }, PlainProbe = class PlainProbe {
765
- _value;
766
- path;
767
- constructor(value, path) {
768
- this._value = value, this.path = path || [];
769
- }
770
- containerType() {
771
- return Array.isArray(this._value) ? "array" : this._value !== null && typeof this._value == "object" ? "object" : "primitive";
772
- }
773
- length() {
774
- if (!Array.isArray(this._value)) throw Error("Won't return length of non-indexable _value");
775
- return this._value.length;
776
- }
777
- getIndex(i) {
778
- return Array.isArray(this._value) ? i >= this.length() ? null : new PlainProbe(this._value[i], this.path.concat(i)) : !1;
779
- }
780
- hasAttribute(key) {
781
- return isRecord$1(this._value) ? this._value.hasOwnProperty(key) : !1;
782
- }
783
- attributeKeys() {
784
- return isRecord$1(this._value) ? Object.keys(this._value) : [];
785
- }
786
- getAttribute(key) {
787
- if (!isRecord$1(this._value)) throw Error("getAttribute only applies to plain objects");
788
- return this.hasAttribute(key) ? new PlainProbe(this._value[key], this.path.concat(key)) : null;
789
- }
790
- get() {
791
- return this._value;
792
- }
793
- };
794
815
  function extractAccessors(path, value) {
795
- let result = [];
796
- return descend(Matcher.fromPath(path).setPayload(function(values) {
797
- result.push(...values);
798
- }), new PlainProbe(value)), result;
816
+ const result = [], matcher = Matcher.fromPath(path).setPayload(function(values) {
817
+ result.push(...values);
818
+ }), accessor = new PlainProbe(value);
819
+ return descend(matcher, accessor), result;
799
820
  }
800
821
  function descend(matcher, accessor) {
801
- let { leads, delivery } = matcher.match(accessor);
802
- leads.forEach((lead) => {
803
- accessorsFromTarget(lead.target, accessor).forEach((childAccessor) => {
804
- descend(lead.matcher, childAccessor);
805
- });
806
- }), delivery && delivery.targets.forEach((target) => {
807
- typeof delivery.payload == "function" && delivery.payload(accessorsFromTarget(target, accessor));
808
- });
822
+ const { leads, delivery } = matcher.match(accessor);
823
+ leads.forEach((lead) => {
824
+ accessorsFromTarget(lead.target, accessor).forEach((childAccessor) => {
825
+ descend(lead.matcher, childAccessor);
826
+ });
827
+ }), delivery && delivery.targets.forEach((target) => {
828
+ typeof delivery.payload == "function" && delivery.payload(accessorsFromTarget(target, accessor));
829
+ });
809
830
  }
810
831
  function accessorsFromTarget(target, accessor) {
811
- let result = [];
812
- if (target.isIndexReference()) target.toIndicies(accessor).forEach((i) => {
813
- result.push(accessor.getIndex(i));
814
- });
815
- else if (target.isAttributeReference()) result.push(accessor.getAttribute(target.name()));
816
- else if (target.isSelfReference()) result.push(accessor);
817
- else throw Error(`Unable to derive accessor for target ${target.toString()}`);
818
- return compact(result);
832
+ const result = [];
833
+ if (target.isIndexReference())
834
+ target.toIndicies(accessor).forEach((i) => {
835
+ result.push(accessor.getIndex(i));
836
+ });
837
+ else if (target.isAttributeReference())
838
+ result.push(accessor.getAttribute(target.name()));
839
+ else if (target.isSelfReference())
840
+ result.push(accessor);
841
+ else
842
+ throw new Error(`Unable to derive accessor for target ${target.toString()}`);
843
+ return compact(result);
819
844
  }
820
- /**
821
- * Extracts values matching the given JsonPath
822
- *
823
- * @param path - Path to extract
824
- * @param value - Value to extract from
825
- * @returns An array of values matching the given path
826
- * @public
827
- */
828
845
  function extract(path, value) {
829
- return extractAccessors(path, value).map((acc) => acc.get());
846
+ return extractAccessors(path, value).map((acc) => acc.get());
830
847
  }
831
- /**
832
- * Extracts a value for the given JsonPath, and includes the specific path of where it was found
833
- *
834
- * @param path - Path to extract
835
- * @param value - Value to extract from
836
- * @returns An array of objects with `path` and `value` keys
837
- * @internal
838
- */
839
848
  function extractWithPath(path, value) {
840
- return extractAccessors(path, value).map((acc) => ({
841
- path: acc.path,
842
- value: acc.get()
843
- }));
849
+ return extractAccessors(path, value).map((acc) => ({ path: acc.path, value: acc.get() }));
850
+ }
851
+ class ImmutableAccessor {
852
+ _value;
853
+ path;
854
+ constructor(value, path) {
855
+ this._value = value, this.path = path || [];
856
+ }
857
+ containerType() {
858
+ return Array.isArray(this._value) ? "array" : this._value !== null && typeof this._value == "object" ? "object" : "primitive";
859
+ }
860
+ valueType() {
861
+ return this._value === null ? "null" : Array.isArray(this._value) ? "array" : typeof this._value;
862
+ }
863
+ // Common reader, supported by all containers
864
+ get() {
865
+ return this._value;
866
+ }
867
+ // Array reader
868
+ length() {
869
+ if (!Array.isArray(this._value))
870
+ throw new Error("Won't return length of non-indexable _value");
871
+ return this._value.length;
872
+ }
873
+ getIndex(i) {
874
+ return Array.isArray(this._value) ? i >= this.length() ? null : new ImmutableAccessor(this._value[i], this.path.concat(i)) : !1;
875
+ }
876
+ // Object reader
877
+ hasAttribute(key) {
878
+ return isRecord(this._value) ? this._value.hasOwnProperty(key) : !1;
879
+ }
880
+ attributeKeys() {
881
+ return isRecord(this._value) ? Object.keys(this._value) : [];
882
+ }
883
+ getAttribute(key) {
884
+ if (!isRecord(this._value))
885
+ throw new Error("getAttribute only applies to plain objects");
886
+ return this.hasAttribute(key) ? new ImmutableAccessor(this._value[key], this.path.concat(key)) : null;
887
+ }
888
+ // Common writer, supported by all containers
889
+ set(value) {
890
+ return value === this._value ? this : new ImmutableAccessor(value, this.path);
891
+ }
892
+ // array writer interface
893
+ setIndex(i, value) {
894
+ if (!Array.isArray(this._value))
895
+ throw new Error("setIndex only applies to arrays");
896
+ if (Object.is(value, this._value[i]))
897
+ return this;
898
+ const nextValue = this._value.slice();
899
+ return nextValue[i] = value, new ImmutableAccessor(nextValue, this.path);
900
+ }
901
+ setIndexAccessor(i, accessor) {
902
+ return this.setIndex(i, accessor.get());
903
+ }
904
+ unsetIndices(indices) {
905
+ if (!Array.isArray(this._value))
906
+ throw new Error("unsetIndices only applies to arrays");
907
+ const length = this._value.length, nextValue = [];
908
+ for (let i = 0; i < length; i++)
909
+ indices.indexOf(i) === -1 && nextValue.push(this._value[i]);
910
+ return new ImmutableAccessor(nextValue, this.path);
911
+ }
912
+ insertItemsAt(pos, items) {
913
+ if (!Array.isArray(this._value))
914
+ throw new Error("insertItemsAt only applies to arrays");
915
+ let nextValue;
916
+ return this._value.length === 0 && pos === 0 ? nextValue = items : nextValue = this._value.slice(0, pos).concat(items).concat(this._value.slice(pos)), new ImmutableAccessor(nextValue, this.path);
917
+ }
918
+ // Object writer interface
919
+ setAttribute(key, value) {
920
+ if (!isRecord(this._value))
921
+ throw new Error("Unable to set attribute of non-object container");
922
+ if (Object.is(value, this._value[key]))
923
+ return this;
924
+ const nextValue = Object.assign({}, this._value, { [key]: value });
925
+ return new ImmutableAccessor(nextValue, this.path);
926
+ }
927
+ setAttributeAccessor(key, accessor) {
928
+ return this.setAttribute(key, accessor.get());
929
+ }
930
+ unsetAttribute(key) {
931
+ if (!isRecord(this._value))
932
+ throw new Error("Unable to unset attribute of non-object container");
933
+ const nextValue = Object.assign({}, this._value);
934
+ return delete nextValue[key], new ImmutableAccessor(nextValue, this.path);
935
+ }
844
936
  }
845
- /**
846
- * An immutable probe/writer for plain JS objects that will never mutate
847
- * the provided _value in place. Each setter returns a new (wrapped) version
848
- * of the value.
849
- */
850
- var ImmutableAccessor = class ImmutableAccessor {
851
- _value;
852
- path;
853
- constructor(value, path) {
854
- this._value = value, this.path = path || [];
855
- }
856
- containerType() {
857
- return Array.isArray(this._value) ? "array" : this._value !== null && typeof this._value == "object" ? "object" : "primitive";
858
- }
859
- valueType() {
860
- return this._value === null ? "null" : Array.isArray(this._value) ? "array" : typeof this._value;
861
- }
862
- get() {
863
- return this._value;
864
- }
865
- length() {
866
- if (!Array.isArray(this._value)) throw Error("Won't return length of non-indexable _value");
867
- return this._value.length;
868
- }
869
- getIndex(i) {
870
- return Array.isArray(this._value) ? i >= this.length() ? null : new ImmutableAccessor(this._value[i], this.path.concat(i)) : !1;
871
- }
872
- hasAttribute(key) {
873
- return isRecord(this._value) ? this._value.hasOwnProperty(key) : !1;
874
- }
875
- attributeKeys() {
876
- return isRecord(this._value) ? Object.keys(this._value) : [];
877
- }
878
- getAttribute(key) {
879
- if (!isRecord(this._value)) throw Error("getAttribute only applies to plain objects");
880
- return this.hasAttribute(key) ? new ImmutableAccessor(this._value[key], this.path.concat(key)) : null;
881
- }
882
- set(value) {
883
- return value === this._value ? this : new ImmutableAccessor(value, this.path);
884
- }
885
- setIndex(i, value) {
886
- if (!Array.isArray(this._value)) throw Error("setIndex only applies to arrays");
887
- if (Object.is(value, this._value[i])) return this;
888
- let nextValue = this._value.slice();
889
- return nextValue[i] = value, new ImmutableAccessor(nextValue, this.path);
890
- }
891
- setIndexAccessor(i, accessor) {
892
- return this.setIndex(i, accessor.get());
893
- }
894
- unsetIndices(indices) {
895
- if (!Array.isArray(this._value)) throw Error("unsetIndices only applies to arrays");
896
- let length = this._value.length, nextValue = [];
897
- for (let i = 0; i < length; i++) indices.indexOf(i) === -1 && nextValue.push(this._value[i]);
898
- return new ImmutableAccessor(nextValue, this.path);
899
- }
900
- insertItemsAt(pos, items) {
901
- if (!Array.isArray(this._value)) throw Error("insertItemsAt only applies to arrays");
902
- let nextValue;
903
- return nextValue = this._value.length === 0 && pos === 0 ? items : this._value.slice(0, pos).concat(items).concat(this._value.slice(pos)), new ImmutableAccessor(nextValue, this.path);
904
- }
905
- setAttribute(key, value) {
906
- if (!isRecord(this._value)) throw Error("Unable to set attribute of non-object container");
907
- if (Object.is(value, this._value[key])) return this;
908
- let nextValue = Object.assign({}, this._value, { [key]: value });
909
- return new ImmutableAccessor(nextValue, this.path);
910
- }
911
- setAttributeAccessor(key, accessor) {
912
- return this.setAttribute(key, accessor.get());
913
- }
914
- unsetAttribute(key) {
915
- if (!isRecord(this._value)) throw Error("Unable to unset attribute of non-object container");
916
- let nextValue = Object.assign({}, this._value);
917
- return delete nextValue[key], new ImmutableAccessor(nextValue, this.path);
918
- }
919
- };
920
937
  function isRecord(value) {
921
- return typeof value == "object" && !!value;
938
+ return value !== null && typeof value == "object";
922
939
  }
923
940
  function applyPatch(patch, oldValue) {
924
- if (typeof oldValue != "string") return oldValue;
925
- let [result] = applyPatches(patch, oldValue, { allowExceedingIndices: !0 });
926
- return result;
941
+ if (typeof oldValue != "string") return oldValue;
942
+ const [result] = applyPatches(patch, oldValue, { allowExceedingIndices: !0 });
943
+ return result;
944
+ }
945
+ class DiffMatchPatch {
946
+ path;
947
+ dmpPatch;
948
+ id;
949
+ constructor(id, path, dmpPatchSrc) {
950
+ this.id = id, this.path = path, this.dmpPatch = parsePatch$1(dmpPatchSrc);
951
+ }
952
+ apply(targets, accessor) {
953
+ let result = accessor;
954
+ if (result.containerType() === "primitive")
955
+ return result;
956
+ for (const target of targets) {
957
+ if (target.isIndexReference()) {
958
+ for (const index of target.toIndicies(accessor)) {
959
+ const item = result.getIndex(index);
960
+ if (!item)
961
+ continue;
962
+ const oldValue = item.get(), nextValue = applyPatch(this.dmpPatch, oldValue);
963
+ result = result.setIndex(index, nextValue);
964
+ }
965
+ continue;
966
+ }
967
+ if (target.isAttributeReference() && result.hasAttribute(target.name())) {
968
+ const attribute = result.getAttribute(target.name());
969
+ if (!attribute)
970
+ continue;
971
+ const oldValue = attribute.get(), nextValue = applyPatch(this.dmpPatch, oldValue);
972
+ result = result.setAttribute(target.name(), nextValue);
973
+ continue;
974
+ }
975
+ throw new Error(`Unable to apply diffMatchPatch to target ${target.toString()}`);
976
+ }
977
+ return result;
978
+ }
927
979
  }
928
- var DiffMatchPatch = class {
929
- path;
930
- dmpPatch;
931
- id;
932
- constructor(id, path, dmpPatchSrc) {
933
- this.id = id, this.path = path, this.dmpPatch = parsePatch(dmpPatchSrc);
934
- }
935
- apply(targets, accessor) {
936
- let result = accessor;
937
- if (result.containerType() === "primitive") return result;
938
- for (let target of targets) {
939
- if (target.isIndexReference()) {
940
- for (let index of target.toIndicies(accessor)) {
941
- let item = result.getIndex(index);
942
- if (!item) continue;
943
- let oldValue = item.get(), nextValue = applyPatch(this.dmpPatch, oldValue);
944
- result = result.setIndex(index, nextValue);
945
- }
946
- continue;
947
- }
948
- if (target.isAttributeReference() && result.hasAttribute(target.name())) {
949
- let attribute = result.getAttribute(target.name());
950
- if (!attribute) continue;
951
- let oldValue = attribute.get(), nextValue = applyPatch(this.dmpPatch, oldValue);
952
- result = result.setAttribute(target.name(), nextValue);
953
- continue;
954
- }
955
- throw Error(`Unable to apply diffMatchPatch to target ${target.toString()}`);
956
- }
957
- return result;
958
- }
959
- };
960
980
  function performIncrement(previousValue, delta) {
961
- return typeof previousValue != "number" || !Number.isFinite(previousValue) ? previousValue : previousValue + delta;
981
+ return typeof previousValue != "number" || !Number.isFinite(previousValue) ? previousValue : previousValue + delta;
982
+ }
983
+ class IncPatch {
984
+ path;
985
+ value;
986
+ id;
987
+ constructor(id, path, value) {
988
+ this.path = path, this.value = value, this.id = id;
989
+ }
990
+ apply(targets, accessor) {
991
+ let result = accessor;
992
+ if (result.containerType() === "primitive")
993
+ return result;
994
+ for (const target of targets) {
995
+ if (target.isIndexReference()) {
996
+ for (const index of target.toIndicies(accessor)) {
997
+ const item = result.getIndex(index);
998
+ if (!item)
999
+ continue;
1000
+ const previousValue = item.get();
1001
+ result = result.setIndex(index, performIncrement(previousValue, this.value));
1002
+ }
1003
+ continue;
1004
+ }
1005
+ if (target.isAttributeReference()) {
1006
+ const attribute = result.getAttribute(target.name());
1007
+ if (!attribute)
1008
+ continue;
1009
+ const previousValue = attribute.get();
1010
+ result = result.setAttribute(target.name(), performIncrement(previousValue, this.value));
1011
+ continue;
1012
+ }
1013
+ throw new Error(`Unable to apply to target ${target.toString()}`);
1014
+ }
1015
+ return result;
1016
+ }
962
1017
  }
963
- var IncPatch = class {
964
- path;
965
- value;
966
- id;
967
- constructor(id, path, value) {
968
- this.path = path, this.value = value, this.id = id;
969
- }
970
- apply(targets, accessor) {
971
- let result = accessor;
972
- if (result.containerType() === "primitive") return result;
973
- for (let target of targets) {
974
- if (target.isIndexReference()) {
975
- for (let index of target.toIndicies(accessor)) {
976
- let item = result.getIndex(index);
977
- if (!item) continue;
978
- let previousValue = item.get();
979
- result = result.setIndex(index, performIncrement(previousValue, this.value));
980
- }
981
- continue;
982
- }
983
- if (target.isAttributeReference()) {
984
- let attribute = result.getAttribute(target.name());
985
- if (!attribute) continue;
986
- let previousValue = attribute.get();
987
- result = result.setAttribute(target.name(), performIncrement(previousValue, this.value));
988
- continue;
989
- }
990
- throw Error(`Unable to apply to target ${target.toString()}`);
991
- }
992
- return result;
993
- }
994
- };
995
1018
  function targetsToIndicies(targets, accessor) {
996
- let result = [];
997
- return targets.forEach((target) => {
998
- target.isIndexReference() && result.push(...target.toIndicies(accessor));
999
- }), result.sort((a, b) => a - b);
1019
+ const result = [];
1020
+ return targets.forEach((target) => {
1021
+ target.isIndexReference() && result.push(...target.toIndicies(accessor));
1022
+ }), result.sort((a, b) => a - b);
1023
+ }
1024
+ class InsertPatch {
1025
+ location;
1026
+ path;
1027
+ items;
1028
+ id;
1029
+ constructor(id, location, path, items) {
1030
+ this.id = id, this.location = location, this.path = path, this.items = items;
1031
+ }
1032
+ apply(targets, accessor) {
1033
+ let result = accessor;
1034
+ if (accessor.containerType() !== "array") {
1035
+ const valueType = accessor.valueType();
1036
+ throw new Error(
1037
+ `Attempt to apply insert patch to value of type "${valueType}" at path "${accessor.path.join(" \u2192 ")}"`
1038
+ );
1039
+ }
1040
+ switch (this.location) {
1041
+ case "before": {
1042
+ const pos = minIndex(targets, accessor);
1043
+ result = result.insertItemsAt(pos, this.items);
1044
+ break;
1045
+ }
1046
+ case "after": {
1047
+ const pos = maxIndex(targets, accessor);
1048
+ result = result.insertItemsAt(pos + 1, this.items);
1049
+ break;
1050
+ }
1051
+ case "replace": {
1052
+ const indicies = targetsToIndicies(targets, accessor);
1053
+ result = result.unsetIndices(indicies), result = result.insertItemsAt(indicies[0], this.items);
1054
+ break;
1055
+ }
1056
+ default:
1057
+ throw new Error(`Unsupported location atm: ${this.location}`);
1058
+ }
1059
+ return result;
1060
+ }
1000
1061
  }
1001
- var InsertPatch = class {
1002
- location;
1003
- path;
1004
- items;
1005
- id;
1006
- constructor(id, location, path, items) {
1007
- this.id = id, this.location = location, this.path = path, this.items = items;
1008
- }
1009
- apply(targets, accessor) {
1010
- let result = accessor;
1011
- if (accessor.containerType() !== "array") {
1012
- let valueType = accessor.valueType();
1013
- throw Error(`Attempt to apply insert patch to value of type "${valueType}" at path "${accessor.path.join(" → ")}"`);
1014
- }
1015
- switch (this.location) {
1016
- case "before": {
1017
- let pos = minIndex(targets, accessor);
1018
- result = result.insertItemsAt(pos, this.items);
1019
- break;
1020
- }
1021
- case "after": {
1022
- let pos = maxIndex(targets, accessor);
1023
- result = result.insertItemsAt(pos + 1, this.items);
1024
- break;
1025
- }
1026
- case "replace": {
1027
- let indicies = targetsToIndicies(targets, accessor);
1028
- result = result.unsetIndices(indicies), result = result.insertItemsAt(indicies[0], this.items);
1029
- break;
1030
- }
1031
- default: throw Error(`Unsupported location atm: ${this.location}`);
1032
- }
1033
- return result;
1034
- }
1035
- };
1036
1062
  function minIndex(targets, accessor) {
1037
- let result = min(targetsToIndicies(targets, accessor)) || 0;
1038
- return targets.forEach((target) => {
1039
- if (target.isRange()) {
1040
- let { start } = target.expandRange();
1041
- start < result && (result = start);
1042
- }
1043
- }), result;
1063
+ let result = min(targetsToIndicies(targets, accessor)) || 0;
1064
+ return targets.forEach((target) => {
1065
+ if (target.isRange()) {
1066
+ const { start } = target.expandRange();
1067
+ start < result && (result = start);
1068
+ }
1069
+ }), result;
1044
1070
  }
1045
1071
  function maxIndex(targets, accessor) {
1046
- let result = max(targetsToIndicies(targets, accessor)) || 0;
1047
- return targets.forEach((target) => {
1048
- if (target.isRange()) {
1049
- let { end } = target.expandRange();
1050
- end > result && (result = end);
1051
- }
1052
- }), result;
1072
+ let result = max(targetsToIndicies(targets, accessor)) || 0;
1073
+ return targets.forEach((target) => {
1074
+ if (target.isRange()) {
1075
+ const { end } = target.expandRange();
1076
+ end > result && (result = end);
1077
+ }
1078
+ }), result;
1053
1079
  }
1054
- var SetIfMissingPatch = class {
1055
- id;
1056
- path;
1057
- value;
1058
- constructor(id, path, value) {
1059
- this.id = id, this.path = path, this.value = value;
1060
- }
1061
- apply(targets, accessor) {
1062
- let result = accessor;
1063
- return targets.forEach((target) => {
1064
- if (!target.isIndexReference()) if (target.isAttributeReference()) result.containerType() === "primitive" ? result = result.set({ [target.name()]: this.value }) : result.hasAttribute(target.name()) || (result = accessor.setAttribute(target.name(), this.value));
1065
- else throw Error(`Unable to apply to target ${target.toString()}`);
1066
- }), result;
1067
- }
1068
- }, SetPatch = class {
1069
- id;
1070
- path;
1071
- value;
1072
- constructor(id, path, value) {
1073
- this.id = id, this.path = path, this.value = value;
1074
- }
1075
- apply(targets, accessor) {
1076
- let result = accessor;
1077
- return targets.forEach((target) => {
1078
- if (target.isSelfReference()) result = result.set(this.value);
1079
- else if (target.isIndexReference()) target.toIndicies(accessor).forEach((i) => {
1080
- result = result.setIndex(i, this.value);
1081
- });
1082
- else if (target.isAttributeReference()) result = result.containerType() === "primitive" ? result.set({ [target.name()]: this.value }) : result.setAttribute(target.name(), this.value);
1083
- else throw Error(`Unable to apply to target ${target.toString()}`);
1084
- }), result;
1085
- }
1086
- }, UnsetPatch = class {
1087
- id;
1088
- path;
1089
- value;
1090
- constructor(id, path) {
1091
- this.id = id, this.path = path;
1092
- }
1093
- apply(targets, accessor) {
1094
- let result = accessor;
1095
- switch (accessor.containerType()) {
1096
- case "array":
1097
- result = result.unsetIndices(targetsToIndicies(targets, accessor));
1098
- break;
1099
- case "object":
1100
- targets.forEach((target) => {
1101
- result = result.unsetAttribute(target.name());
1102
- });
1103
- break;
1104
- default: throw Error("Target value is neither indexable or an object. This error should potentially just be silently ignored?");
1105
- }
1106
- return result;
1107
- }
1108
- };
1109
- function parsePatch$1(patch) {
1110
- let result = [];
1111
- if (Array.isArray(patch)) return patch.reduce((r, p) => r.concat(parsePatch$1(p)), result);
1112
- let { set, setIfMissing, unset, diffMatchPatch, inc, dec, insert } = patch;
1113
- if (setIfMissing && Object.keys(setIfMissing).forEach((path) => {
1114
- result.push(new SetIfMissingPatch(patch.id, path, setIfMissing[path]));
1115
- }), set && Object.keys(set).forEach((path) => {
1116
- result.push(new SetPatch(patch.id, path, set[path]));
1117
- }), unset && unset.forEach((path) => {
1118
- result.push(new UnsetPatch(patch.id, path));
1119
- }), diffMatchPatch && Object.keys(diffMatchPatch).forEach((path) => {
1120
- result.push(new DiffMatchPatch(patch.id, path, diffMatchPatch[path]));
1121
- }), inc && Object.keys(inc).forEach((path) => {
1122
- result.push(new IncPatch(patch.id, path, inc[path]));
1123
- }), dec && Object.keys(dec).forEach((path) => {
1124
- result.push(new IncPatch(patch.id, path, -dec[path]));
1125
- }), insert) {
1126
- let location, path, spec = insert;
1127
- if ("before" in spec) location = "before", path = spec.before;
1128
- else if ("after" in spec) location = "after", path = spec.after;
1129
- else if ("replace" in spec) location = "replace", path = spec.replace;
1130
- else throw Error("Invalid insert patch");
1131
- result.push(new InsertPatch(patch.id, location, path, spec.items));
1132
- }
1133
- return result;
1080
+ class SetIfMissingPatch {
1081
+ id;
1082
+ path;
1083
+ value;
1084
+ constructor(id, path, value) {
1085
+ this.id = id, this.path = path, this.value = value;
1086
+ }
1087
+ apply(targets, accessor) {
1088
+ let result = accessor;
1089
+ return targets.forEach((target) => {
1090
+ if (!target.isIndexReference())
1091
+ if (target.isAttributeReference())
1092
+ result.containerType() === "primitive" ? result = result.set({ [target.name()]: this.value }) : result.hasAttribute(target.name()) || (result = accessor.setAttribute(target.name(), this.value));
1093
+ else
1094
+ throw new Error(`Unable to apply to target ${target.toString()}`);
1095
+ }), result;
1096
+ }
1097
+ }
1098
+ class SetPatch {
1099
+ id;
1100
+ path;
1101
+ value;
1102
+ constructor(id, path, value) {
1103
+ this.id = id, this.path = path, this.value = value;
1104
+ }
1105
+ apply(targets, accessor) {
1106
+ let result = accessor;
1107
+ return targets.forEach((target) => {
1108
+ if (target.isSelfReference())
1109
+ result = result.set(this.value);
1110
+ else if (target.isIndexReference())
1111
+ target.toIndicies(accessor).forEach((i) => {
1112
+ result = result.setIndex(i, this.value);
1113
+ });
1114
+ else if (target.isAttributeReference())
1115
+ result.containerType() === "primitive" ? result = result.set({ [target.name()]: this.value }) : result = result.setAttribute(target.name(), this.value);
1116
+ else
1117
+ throw new Error(`Unable to apply to target ${target.toString()}`);
1118
+ }), result;
1119
+ }
1120
+ }
1121
+ class UnsetPatch {
1122
+ id;
1123
+ path;
1124
+ value;
1125
+ constructor(id, path) {
1126
+ this.id = id, this.path = path;
1127
+ }
1128
+ apply(targets, accessor) {
1129
+ let result = accessor;
1130
+ switch (accessor.containerType()) {
1131
+ case "array":
1132
+ result = result.unsetIndices(targetsToIndicies(targets, accessor));
1133
+ break;
1134
+ case "object":
1135
+ targets.forEach((target) => {
1136
+ result = result.unsetAttribute(target.name());
1137
+ });
1138
+ break;
1139
+ default:
1140
+ throw new Error(
1141
+ "Target value is neither indexable or an object. This error should potentially just be silently ignored?"
1142
+ );
1143
+ }
1144
+ return result;
1145
+ }
1146
+ }
1147
+ function parsePatch(patch) {
1148
+ const result = [];
1149
+ if (Array.isArray(patch))
1150
+ return patch.reduce((r, p) => r.concat(parsePatch(p)), result);
1151
+ const { set, setIfMissing, unset, diffMatchPatch, inc, dec, insert } = patch;
1152
+ if (setIfMissing && Object.keys(setIfMissing).forEach((path) => {
1153
+ result.push(new SetIfMissingPatch(patch.id, path, setIfMissing[path]));
1154
+ }), set && Object.keys(set).forEach((path) => {
1155
+ result.push(new SetPatch(patch.id, path, set[path]));
1156
+ }), unset && unset.forEach((path) => {
1157
+ result.push(new UnsetPatch(patch.id, path));
1158
+ }), diffMatchPatch && Object.keys(diffMatchPatch).forEach((path) => {
1159
+ result.push(new DiffMatchPatch(patch.id, path, diffMatchPatch[path]));
1160
+ }), inc && Object.keys(inc).forEach((path) => {
1161
+ result.push(new IncPatch(patch.id, path, inc[path]));
1162
+ }), dec && Object.keys(dec).forEach((path) => {
1163
+ result.push(new IncPatch(patch.id, path, -dec[path]));
1164
+ }), insert) {
1165
+ let location, path;
1166
+ const spec = insert;
1167
+ if ("before" in spec)
1168
+ location = "before", path = spec.before;
1169
+ else if ("after" in spec)
1170
+ location = "after", path = spec.after;
1171
+ else if ("replace" in spec)
1172
+ location = "replace", path = spec.replace;
1173
+ else
1174
+ throw new Error("Invalid insert patch");
1175
+ result.push(new InsertPatch(patch.id, location, path, spec.items));
1176
+ }
1177
+ return result;
1178
+ }
1179
+ class Patcher {
1180
+ patches;
1181
+ constructor(patch) {
1182
+ this.patches = parsePatch(patch);
1183
+ }
1184
+ apply(value) {
1185
+ const accessor = new ImmutableAccessor(value);
1186
+ return this.applyViaAccessor(accessor).get();
1187
+ }
1188
+ // If you want to use your own accessor implementation, you can use this method
1189
+ // to invoke the patcher. Since all subsequent accessors for children of this accessor
1190
+ // are obtained through the methods in the accessors, you retain full control of the
1191
+ // implementation throguhgout the application. Have a look in ImmutableAccessor
1192
+ // to see an example of how accessors are implemented.
1193
+ applyViaAccessor(accessor) {
1194
+ let result = accessor;
1195
+ const idAccessor = accessor.getAttribute("_id");
1196
+ if (!idAccessor)
1197
+ throw new Error("Cannot apply patch to document with no _id");
1198
+ const id = idAccessor.get();
1199
+ for (const patch of this.patches) {
1200
+ if (patch.id !== id)
1201
+ continue;
1202
+ const matcher = Matcher.fromPath(patch.path).setPayload(patch);
1203
+ result = process(matcher, result);
1204
+ }
1205
+ return result;
1206
+ }
1134
1207
  }
1135
- var Patcher = class {
1136
- patches;
1137
- constructor(patch) {
1138
- this.patches = parsePatch$1(patch);
1139
- }
1140
- apply(value) {
1141
- let accessor = new ImmutableAccessor(value);
1142
- return this.applyViaAccessor(accessor).get();
1143
- }
1144
- applyViaAccessor(accessor) {
1145
- let result = accessor, idAccessor = accessor.getAttribute("_id");
1146
- if (!idAccessor) throw Error("Cannot apply patch to document with no _id");
1147
- let id = idAccessor.get();
1148
- for (let patch of this.patches) patch.id === id && (result = process(Matcher.fromPath(patch.path).setPayload(patch), result));
1149
- return result;
1150
- }
1151
- };
1152
1208
  function process(matcher, accessor) {
1153
- let isSetPatch = matcher.payload instanceof SetPatch || matcher.payload instanceof SetIfMissingPatch, result = accessor, { leads, delivery } = matcher.match(accessor);
1154
- return leads.forEach((lead) => {
1155
- if (lead.target.isIndexReference()) lead.target.toIndicies().forEach((i) => {
1156
- let item = result.getIndex(i);
1157
- if (!item) throw Error("Index out of bounds");
1158
- result = result.setIndexAccessor(i, process(lead.matcher, item));
1159
- });
1160
- else if (lead.target.isAttributeReference()) {
1161
- isSetPatch && result.containerType() === "primitive" && (result = result.set({}));
1162
- let oldValueAccessor = result.getAttribute(lead.target.name());
1163
- if (!oldValueAccessor && isSetPatch && (result = result.setAttribute(lead.target.name(), {}), oldValueAccessor = result.getAttribute(lead.target.name())), !oldValueAccessor) return;
1164
- let newValueAccessor = process(lead.matcher, oldValueAccessor);
1165
- oldValueAccessor !== newValueAccessor && (result = result.setAttributeAccessor(lead.target.name(), newValueAccessor));
1166
- } else throw Error(`Unable to handle target ${lead.target.toString()}`);
1167
- }), delivery && isPatcher(delivery.payload) && (result = delivery.payload.apply(delivery.targets, result)), result;
1209
+ const isSetPatch = matcher.payload instanceof SetPatch || matcher.payload instanceof SetIfMissingPatch;
1210
+ let result = accessor;
1211
+ const { leads, delivery } = matcher.match(accessor);
1212
+ return leads.forEach((lead) => {
1213
+ if (lead.target.isIndexReference())
1214
+ lead.target.toIndicies().forEach((i) => {
1215
+ const item = result.getIndex(i);
1216
+ if (!item)
1217
+ throw new Error("Index out of bounds");
1218
+ result = result.setIndexAccessor(i, process(lead.matcher, item));
1219
+ });
1220
+ else if (lead.target.isAttributeReference()) {
1221
+ isSetPatch && result.containerType() === "primitive" && (result = result.set({}));
1222
+ let oldValueAccessor = result.getAttribute(lead.target.name());
1223
+ if (!oldValueAccessor && isSetPatch && (result = result.setAttribute(lead.target.name(), {}), oldValueAccessor = result.getAttribute(lead.target.name())), !oldValueAccessor)
1224
+ return;
1225
+ const newValueAccessor = process(lead.matcher, oldValueAccessor);
1226
+ oldValueAccessor !== newValueAccessor && (result = result.setAttributeAccessor(lead.target.name(), newValueAccessor));
1227
+ } else
1228
+ throw new Error(`Unable to handle target ${lead.target.toString()}`);
1229
+ }), delivery && isPatcher(delivery.payload) && (result = delivery.payload.apply(delivery.targets, result)), result;
1168
1230
  }
1169
1231
  function isPatcher(payload) {
1170
- return !!(payload && typeof payload == "object" && payload && "apply" in payload && typeof payload.apply == "function");
1232
+ return !!(payload && typeof payload == "object" && payload !== null && "apply" in payload && typeof payload.apply == "function");
1171
1233
  }
1172
- /**
1173
- * Locally unique id's. We use this to generate transaction ids, and they don't have to be
1174
- * cryptographically unique, as the worst that can happen is that they get rejected because
1175
- * of a collision, and then we should just retry with a new id.
1176
- */
1177
1234
  const luid = uuid;
1178
- /**
1179
- * A mutation describing a number of operations on a single document.
1180
- * This should be considered an immutable structure. Mutations are compiled
1181
- * on first application, and any changes in properties will not effectively
1182
- * change its behavior after that.
1183
- *
1184
- * @internal
1185
- */
1186
- var Mutation = class Mutation {
1187
- params;
1188
- compiled;
1189
- _appliesToMissingDocument;
1190
- constructor(options) {
1191
- this.params = options;
1192
- }
1193
- get transactionId() {
1194
- return this.params.transactionId;
1195
- }
1196
- get transition() {
1197
- return this.params.transition;
1198
- }
1199
- get identity() {
1200
- return this.params.identity;
1201
- }
1202
- get previousRev() {
1203
- return this.params.previousRev;
1204
- }
1205
- get resultRev() {
1206
- return this.params.resultRev;
1207
- }
1208
- get mutations() {
1209
- return this.params.mutations;
1210
- }
1211
- get timestamp() {
1212
- if (typeof this.params.timestamp == "string") return new Date(this.params.timestamp);
1213
- }
1214
- get effects() {
1215
- return this.params.effects;
1216
- }
1217
- assignRandomTransactionId() {
1218
- this.params.transactionId = luid(), this.params.resultRev = this.params.transactionId;
1219
- }
1220
- appliesToMissingDocument() {
1221
- if (this._appliesToMissingDocument !== void 0) return this._appliesToMissingDocument;
1222
- let firstMut = this.mutations[0];
1223
- return firstMut ? this._appliesToMissingDocument = !!(firstMut.create || firstMut.createIfNotExists || firstMut.createOrReplace) : this._appliesToMissingDocument = !0, this._appliesToMissingDocument;
1224
- }
1225
- compile() {
1226
- let operations = [], getGuaranteedCreatedAt = (doc) => doc?._createdAt || this.params.timestamp || (/* @__PURE__ */ new Date()).toISOString();
1227
- this.mutations.forEach((mutation) => {
1228
- if (mutation.create) {
1229
- let create = mutation.create || {};
1230
- operations.push((doc) => doc || Object.assign(create, { _createdAt: getGuaranteedCreatedAt(create) }));
1231
- return;
1232
- }
1233
- if (mutation.createIfNotExists) {
1234
- let createIfNotExists = mutation.createIfNotExists || {};
1235
- operations.push((doc) => doc === null ? Object.assign(createIfNotExists, { _createdAt: getGuaranteedCreatedAt(createIfNotExists) }) : doc);
1236
- return;
1237
- }
1238
- if (mutation.createOrReplace) {
1239
- let createOrReplace = mutation.createOrReplace || {};
1240
- operations.push(() => Object.assign(createOrReplace, { _createdAt: getGuaranteedCreatedAt(createOrReplace) }));
1241
- return;
1242
- }
1243
- if (mutation.delete) {
1244
- operations.push(() => null);
1245
- return;
1246
- }
1247
- if (mutation.patch) {
1248
- if ("query" in mutation.patch) return;
1249
- let patch = new Patcher(mutation.patch);
1250
- operations.push((doc) => patch.apply(doc));
1251
- return;
1252
- }
1253
- throw Error(`Unsupported mutation ${JSON.stringify(mutation, null, 2)}`);
1254
- }), typeof this.params.timestamp == "string" && operations.push((doc) => doc ? Object.assign(doc, { _updatedAt: this.params.timestamp }) : null);
1255
- let prevRev = this.previousRev, rev = this.resultRev || this.transactionId;
1256
- this.compiled = (doc) => {
1257
- if (prevRev && doc && prevRev !== doc._rev) throw Error(`Previous revision for this mutation was ${prevRev}, but the document revision is ${doc._rev}`);
1258
- let result = doc;
1259
- for (let operation of operations) result = operation(result);
1260
- return result && rev && (result === doc && (result = Object.assign({}, doc)), result._rev = rev), result;
1261
- };
1262
- }
1263
- apply(document) {
1264
- debug("Applying mutation %O to document %O", this.mutations, document), this.compiled || this.compile();
1265
- let result = this.compiled(document);
1266
- return debug(" => %O", result), result;
1267
- }
1268
- static applyAll(document, mutations) {
1269
- return mutations.reduce((doc, mutation) => mutation.apply(doc), document);
1270
- }
1271
- static squash(document, mutations) {
1272
- let squashed = mutations.reduce((result, mutation) => result.concat(...mutation.mutations), []);
1273
- return new Mutation({ mutations: squashed });
1274
- }
1275
- }, Document = class {
1276
- /**
1277
- * Incoming patches from the server waiting to be applied to HEAD
1278
- */
1279
- incoming = [];
1280
- /**
1281
- * Patches we know has been subitted to the server, but has not been seen yet in the return channel
1282
- * so we can't be sure about the ordering yet (someone else might have slipped something between them)
1283
- */
1284
- submitted = [];
1285
- /**
1286
- * Pending mutations
1287
- */
1288
- pending = [];
1289
- /**
1290
- * Our model of the document according to the incoming patches from the server
1291
- */
1292
- HEAD;
1293
- /**
1294
- * Our optimistic model of what the document will probably look like as soon as all our patches
1295
- * have been processed. Updated every time we stage a new mutation, but also might revert back
1296
- * to previous states if our mutations fail, or could change if unexpected mutations arrive
1297
- * between our own. The `onRebase` callback will be called when EDGE changes in this manner.
1298
- */
1299
- EDGE;
1300
- /**
1301
- * Called with the EDGE document when that document changes for a reason other than us staging
1302
- * a new patch or receiving a mutation from the server while our EDGE is in sync with HEAD:
1303
- * I.e. when EDGE changes because the order of mutations has changed in relation to our
1304
- * optimistic predictions.
1305
- */
1306
- onRebase;
1307
- /**
1308
- * Called when we receive a patch in the normal order of things, but the mutation is not ours
1309
- */
1310
- onMutation;
1311
- /**
1312
- * Called when consistency state changes with the boolean value of the current consistency state
1313
- */
1314
- onConsistencyChanged;
1315
- /**
1316
- * Called whenever a new incoming mutation comes in. These are always ordered correctly.
1317
- */
1318
- onRemoteMutation;
1319
- /**
1320
- * We are consistent when there are no unresolved mutations of our own, and no un-applicable
1321
- * incoming mutations. When this has been going on for too long, and there has been a while
1322
- * since we staged a new mutation, it is time to reset your state.
1323
- */
1324
- inconsistentAt = null;
1325
- /**
1326
- * The last time we staged a patch of our own. If we have been inconsistent for a while, but it
1327
- * hasn't been long since we staged a new mutation, the reason is probably just because the user
1328
- * is typing or something.
1329
- *
1330
- * Should be used as a guard against resetting state for inconsistency reasons.
1331
- */
1332
- lastStagedAt = null;
1333
- constructor(doc) {
1334
- this.reset(doc), this.HEAD = doc, this.EDGE = doc;
1335
- }
1336
- reset(doc) {
1337
- this.incoming = [], this.submitted = [], this.pending = [], this.inconsistentAt = null, this.HEAD = doc, this.EDGE = doc, this.considerIncoming(), this.updateConsistencyFlag();
1338
- }
1339
- arrive(mutation) {
1340
- this.incoming.push(mutation), this.considerIncoming(), this.updateConsistencyFlag();
1341
- }
1342
- stage(mutation, silent) {
1343
- if (!mutation.transactionId) throw Error("Mutations _must_ have transactionId when submitted");
1344
- this.lastStagedAt = /* @__PURE__ */ new Date(), debug("Staging mutation %s (pushed to pending)", mutation.transactionId), this.pending.push(mutation), this.EDGE = mutation.apply(this.EDGE), this.onMutation && !silent && this.onMutation({
1345
- mutation,
1346
- document: this.EDGE,
1347
- remote: !1
1348
- });
1349
- let txnId = mutation.transactionId;
1350
- return this.updateConsistencyFlag(), {
1351
- success: () => {
1352
- this.pendingSuccessfullySubmitted(txnId), this.updateConsistencyFlag();
1353
- },
1354
- failure: () => {
1355
- this.pendingFailed(txnId), this.updateConsistencyFlag();
1356
- }
1357
- };
1358
- }
1359
- isConsistent() {
1360
- return !this.inconsistentAt;
1361
- }
1362
- considerIncoming() {
1363
- let mustRebase = !1, nextMut, rebaseMutations = [];
1364
- if (this.HEAD && this.HEAD._updatedAt) {
1365
- let updatedAt = new Date(this.HEAD._updatedAt);
1366
- this.incoming.find((mut) => mut.timestamp && mut.timestamp < updatedAt) && (this.incoming = this.incoming.filter((mut) => mut.timestamp && mut.timestamp < updatedAt));
1367
- }
1368
- let protect = 0;
1369
- do {
1370
- if (this.HEAD) {
1371
- let HEAD = this.HEAD;
1372
- nextMut = HEAD._rev ? this.incoming.find((mut) => mut.previousRev === HEAD._rev) : void 0;
1373
- } else nextMut = this.incoming.find((mut) => mut.appliesToMissingDocument());
1374
- if (nextMut) {
1375
- let applied = this.applyIncoming(nextMut);
1376
- if (mustRebase ||= applied, mustRebase && rebaseMutations.push(nextMut), protect++ > 10) throw Error(`Mutator stuck flushing incoming mutations. Probably stuck here: ${JSON.stringify(nextMut)}`);
1377
- }
1378
- } while (nextMut);
1379
- this.incoming.length > 0 && debug.enabled && debug("Unable to apply mutations %s", this.incoming.map((mut) => mut.transactionId).join(", ")), mustRebase && this.rebase(rebaseMutations);
1380
- }
1381
- updateConsistencyFlag() {
1382
- let wasConsistent = this.isConsistent(), isConsistent = this.pending.length === 0 && this.submitted.length === 0 && this.incoming.length === 0;
1383
- isConsistent ? this.inconsistentAt = null : this.inconsistentAt ||= /* @__PURE__ */ new Date(), wasConsistent != isConsistent && this.onConsistencyChanged && (debug(isConsistent ? "Buffered document is inconsistent" : "Buffered document is consistent"), this.onConsistencyChanged(isConsistent));
1384
- }
1385
- applyIncoming(mut) {
1386
- if (!mut) return !1;
1387
- if (!mut.transactionId) throw Error("Received incoming mutation without a transaction ID");
1388
- if (debug("Applying mutation %s -> %s to rev %s", mut.previousRev, mut.resultRev, this.HEAD && this.HEAD._rev), this.HEAD = mut.apply(this.HEAD), this.onRemoteMutation && this.onRemoteMutation(mut), this.incoming = this.incoming.filter((m) => m.transactionId !== mut.transactionId), this.hasUnresolvedMutations()) {
1389
- let needRebase = this.consumeUnresolved(mut.transactionId);
1390
- return debug.enabled && (debug(`Incoming mutation ${mut.transactionId} appeared while there were pending or submitted local mutations`), debug(`Submitted txnIds: ${this.submitted.map((m) => m.transactionId).join(", ")}`), debug(`Pending txnIds: ${this.pending.map((m) => m.transactionId).join(", ")}`), debug("needRebase === %s", needRebase)), needRebase;
1391
- }
1392
- return debug("Remote mutation %s arrived w/o any pending or submitted local mutations", mut.transactionId), this.EDGE = this.HEAD, this.onMutation && this.onMutation({
1393
- mutation: mut,
1394
- document: this.EDGE,
1395
- remote: !0
1396
- }), !1;
1397
- }
1398
- /**
1399
- * Returns true if there are unresolved mutations between HEAD and EDGE, meaning we have
1400
- * mutations that are still waiting to be either submitted, or to be confirmed by the server.
1401
- *
1402
- * @returns true if there are unresolved mutations between HEAD and EDGE, false otherwise
1403
- */
1404
- hasUnresolvedMutations() {
1405
- return this.submitted.length > 0 || this.pending.length > 0;
1406
- }
1407
- /**
1408
- * When an incoming mutation is applied to HEAD, this is called to remove the mutation from
1409
- * the unresolved state. If the newly applied patch is the next upcoming unresolved mutation,
1410
- * no rebase is needed, but we might have the wrong idea about the ordering of mutations, so in
1411
- * that case we are given the flag `needRebase` to tell us that this mutation arrived out of
1412
- * order in terms of our optimistic version, so a rebase is needed.
1413
- *
1414
- * @param txnId - Transaction ID of the remote mutation
1415
- * @returns true if rebase is needed, false otherwise
1416
- */
1417
- consumeUnresolved(txnId) {
1418
- if (this.submitted.length === 0 && this.pending.length === 0) return !1;
1419
- if (this.submitted.length !== 0) {
1420
- if (this.submitted[0].transactionId === txnId) return debug("Remote mutation %s matches upcoming submitted mutation, consumed from 'submitted' buffer", txnId), this.submitted.shift(), !1;
1421
- } else if (this.pending.length > 0 && this.pending[0].transactionId === txnId) return debug("Remote mutation %s matches upcoming pending mutation, consumed from 'pending' buffer", txnId), this.pending.shift(), !1;
1422
- return debug("The mutation was not the upcoming mutation, scrubbing. Pending: %d, Submitted: %d", this.pending.length, this.submitted.length), this.submitted = this.submitted.filter((mut) => mut.transactionId !== txnId), this.pending = this.pending.filter((mut) => mut.transactionId !== txnId), debug("After scrubbing: Pending: %d, Submitted: %d", this.pending.length, this.submitted.length), !0;
1423
- }
1424
- pendingSuccessfullySubmitted(pendingTxnId) {
1425
- if (this.pending.length === 0) return;
1426
- let first = this.pending[0];
1427
- if (first.transactionId === pendingTxnId) {
1428
- this.pending.shift(), this.submitted.push(first);
1429
- return;
1430
- }
1431
- let justSubmitted, stillPending = [];
1432
- this.pending.forEach((mutation) => {
1433
- if (mutation.transactionId === pendingTxnId) {
1434
- justSubmitted = mutation;
1435
- return;
1436
- }
1437
- stillPending.push(mutation);
1438
- }), justSubmitted && this.submitted.push(justSubmitted), this.pending = stillPending, this.rebase([]);
1439
- }
1440
- pendingFailed(pendingTxnId) {
1441
- this.pending = this.pending.filter((mutation) => mutation.transactionId !== pendingTxnId), this.rebase([]);
1442
- }
1443
- rebase(incomingMutations) {
1444
- let oldEdge = this.EDGE;
1445
- this.EDGE = Mutation.applyAll(this.HEAD, this.submitted.concat(this.pending)), oldEdge !== null && this.EDGE !== null && (oldEdge._rev = this.EDGE._rev), !isEqual(this.EDGE, oldEdge) && this.onRebase && this.onRebase(this.EDGE, incomingMutations, this.pending);
1446
- }
1447
- }, SquashingBuffer = class {
1448
- /**
1449
- * The document forming the basis of this squash
1450
- */
1451
- BASIS;
1452
- /**
1453
- * The document after the out-Mutation has been applied, but before the staged
1454
- * operations are committed.
1455
- */
1456
- PRESTAGE;
1457
- /**
1458
- * setOperations contain the latest set operation by path. If the set-operations are
1459
- * updating strings to new strings, they are rewritten as diffMatchPatch operations,
1460
- * any new set operations on the same paths overwrites any older set operations.
1461
- * Only set-operations assigning plain values to plain values gets optimized like this.
1462
- */
1463
- setOperations;
1464
- /**
1465
- * `documentPresent` is true whenever we know that the document must be present due
1466
- * to preceeding mutations. `false` implies that it may or may not already exist.
1467
- */
1468
- documentPresent;
1469
- /**
1470
- * The operations in the out-Mutation are not able to be optimized any further
1471
- */
1472
- out = [];
1473
- /**
1474
- * Staged mutation operations
1475
- */
1476
- staged;
1477
- constructor(doc) {
1478
- doc ? debug("Reset mutation buffer to rev %s", doc._rev) : debug("Reset mutation buffer state to document being deleted"), this.staged = [], this.setOperations = {}, this.documentPresent = !1, this.BASIS = doc, this.PRESTAGE = doc;
1479
- }
1480
- add(mut) {
1481
- mut.mutations.forEach((op) => this.addOperation(op));
1482
- }
1483
- hasChanges() {
1484
- return this.out.length > 0 || Object.keys(this.setOperations).length > 0;
1485
- }
1486
- /**
1487
- * Extracts the mutations in this buffer.
1488
- * After this is done, the buffer lifecycle is over and the client should
1489
- * create an new one with the new, updated BASIS.
1490
- *
1491
- * @param txnId - Transaction ID
1492
- * @returns A `Mutation` instance if we had outgoing mutations pending, null otherwise
1493
- */
1494
- purge(txnId) {
1495
- this.stashStagedOperations();
1496
- let result = null;
1497
- return this.out.length > 0 && (debug("Purged mutation buffer"), result = new Mutation({
1498
- mutations: this.out,
1499
- resultRev: txnId,
1500
- transactionId: txnId
1501
- })), this.out = [], this.documentPresent = !1, result;
1502
- }
1503
- addOperation(op) {
1504
- if (op.patch && op.patch.set && "id" in op.patch && op.patch.id === this.PRESTAGE?._id && Object.keys(op.patch).length === 2) {
1505
- let setPatch = op.patch.set, unoptimizable = {};
1506
- for (let path of Object.keys(setPatch)) setPatch.hasOwnProperty(path) && (this.optimiseSetOperation(path, setPatch[path]) || (unoptimizable[path] = setPatch[path]));
1507
- Object.keys(unoptimizable).length > 0 && (debug("Unoptimizable set-operation detected, purging optimization buffer"), this.staged.push({ patch: {
1508
- id: this.PRESTAGE._id,
1509
- set: unoptimizable
1510
- } }), this.stashStagedOperations());
1511
- return;
1512
- }
1513
- if (op.createIfNotExists && this.PRESTAGE && op.createIfNotExists._id === this.PRESTAGE._id) {
1514
- this.documentPresent || (this.staged.push(op), this.documentPresent = !0, this.stashStagedOperations());
1515
- return;
1516
- }
1517
- debug("Unoptimizable mutation detected, purging optimization buffer"), this.staged.push(op), this.stashStagedOperations();
1518
- }
1519
- /**
1520
- * Attempt to perform one single set operation in an optimised manner, return value
1521
- * reflects whether or not the operation could be performed.
1522
-
1523
- * @param path - The JSONPath to the set operation in question
1524
- * @param nextValue - The value to be set
1525
- * @returns True of optimized, false otherwise
1526
- */
1527
- optimiseSetOperation(path, nextValue) {
1528
- if (typeof nextValue == "object") return !1;
1529
- let matches = extractWithPath(path, this.PRESTAGE);
1530
- if (matches.length !== 1) return !1;
1531
- let match = matches[0];
1532
- if (typeof match.value == "object" || !this.PRESTAGE) return !1;
1533
- let op = null;
1534
- if (match.value === nextValue) op = null;
1535
- else if (typeof match.value == "string" && typeof nextValue == "string") try {
1536
- let patch = stringifyPatches(makePatches(match.value, nextValue));
1537
- op = { patch: {
1538
- id: this.PRESTAGE._id,
1539
- diffMatchPatch: { [path]: patch }
1540
- } };
1541
- } catch {
1542
- return !1;
1543
- }
1544
- else op = { patch: {
1545
- id: this.PRESTAGE._id,
1546
- set: { [path]: nextValue }
1547
- } };
1548
- let canonicalPath = arrayToJSONMatchPath(match.path);
1549
- return op ? this.setOperations[canonicalPath] = op : delete this.setOperations[canonicalPath], !0;
1550
- }
1551
- stashStagedOperations() {
1552
- let nextOps = [];
1553
- Object.keys(this.setOperations).forEach((key) => {
1554
- let op = this.setOperations[key];
1555
- op && nextOps.push(op);
1556
- }), nextOps.push(...this.staged), nextOps.length > 0 && (this.PRESTAGE = new Mutation({ mutations: nextOps }).apply(this.PRESTAGE), this.staged = [], this.setOperations = {}), this.out.push(...nextOps);
1557
- }
1558
- /**
1559
- * Rebases given the new base-document
1560
- *
1561
- * @param newBasis - New base document to rebase on
1562
- * @returns New "edge" document with buffered changes integrated
1563
- */
1564
- rebase(newBasis) {
1565
- return this.stashStagedOperations(), newBasis === null ? (this.out = [], this.BASIS = newBasis, this.PRESTAGE = newBasis, this.documentPresent = !1) : (this.BASIS = newBasis, this.out ? this.PRESTAGE = new Mutation({ mutations: this.out }).apply(this.BASIS) : this.PRESTAGE = this.BASIS), this.PRESTAGE;
1566
- }
1567
- }, Commit = class {
1568
- mutations;
1569
- tries;
1570
- resolve;
1571
- reject;
1572
- constructor(mutations, { resolve, reject }) {
1573
- this.mutations = mutations, this.tries = 0, this.resolve = resolve, this.reject = reject;
1574
- }
1575
- apply(doc) {
1576
- return Mutation.applyAll(doc, this.mutations);
1577
- }
1578
- squash(doc) {
1579
- let result = Mutation.squash(doc, this.mutations);
1580
- return result.assignRandomTransactionId(), result;
1581
- }
1582
- };
1235
+ class Mutation {
1236
+ params;
1237
+ compiled;
1238
+ _appliesToMissingDocument;
1239
+ constructor(options) {
1240
+ this.params = options;
1241
+ }
1242
+ get transactionId() {
1243
+ return this.params.transactionId;
1244
+ }
1245
+ get transition() {
1246
+ return this.params.transition;
1247
+ }
1248
+ get identity() {
1249
+ return this.params.identity;
1250
+ }
1251
+ get previousRev() {
1252
+ return this.params.previousRev;
1253
+ }
1254
+ get resultRev() {
1255
+ return this.params.resultRev;
1256
+ }
1257
+ get mutations() {
1258
+ return this.params.mutations;
1259
+ }
1260
+ get timestamp() {
1261
+ if (typeof this.params.timestamp == "string")
1262
+ return new Date(this.params.timestamp);
1263
+ }
1264
+ get effects() {
1265
+ return this.params.effects;
1266
+ }
1267
+ assignRandomTransactionId() {
1268
+ this.params.transactionId = luid(), this.params.resultRev = this.params.transactionId;
1269
+ }
1270
+ appliesToMissingDocument() {
1271
+ if (typeof this._appliesToMissingDocument < "u")
1272
+ return this._appliesToMissingDocument;
1273
+ const firstMut = this.mutations[0];
1274
+ return firstMut ? this._appliesToMissingDocument = !!(firstMut.create || firstMut.createIfNotExists || firstMut.createOrReplace) : this._appliesToMissingDocument = !0, this._appliesToMissingDocument;
1275
+ }
1276
+ // Compiles all mutations into a handy function
1277
+ compile() {
1278
+ const operations = [], getGuaranteedCreatedAt = (doc) => doc?._createdAt || this.params.timestamp || (/* @__PURE__ */ new Date()).toISOString();
1279
+ this.mutations.forEach((mutation) => {
1280
+ if (mutation.create) {
1281
+ const create = mutation.create || {};
1282
+ operations.push((doc) => doc || Object.assign(create, {
1283
+ _createdAt: getGuaranteedCreatedAt(create)
1284
+ }));
1285
+ return;
1286
+ }
1287
+ if (mutation.createIfNotExists) {
1288
+ const createIfNotExists = mutation.createIfNotExists || {};
1289
+ operations.push(
1290
+ (doc) => doc === null ? Object.assign(createIfNotExists, {
1291
+ _createdAt: getGuaranteedCreatedAt(createIfNotExists)
1292
+ }) : doc
1293
+ );
1294
+ return;
1295
+ }
1296
+ if (mutation.createOrReplace) {
1297
+ const createOrReplace = mutation.createOrReplace || {};
1298
+ operations.push(
1299
+ () => Object.assign(createOrReplace, {
1300
+ _createdAt: getGuaranteedCreatedAt(createOrReplace)
1301
+ })
1302
+ );
1303
+ return;
1304
+ }
1305
+ if (mutation.delete) {
1306
+ operations.push(() => null);
1307
+ return;
1308
+ }
1309
+ if (mutation.patch) {
1310
+ if ("query" in mutation.patch)
1311
+ return;
1312
+ const patch = new Patcher(mutation.patch);
1313
+ operations.push((doc) => patch.apply(doc));
1314
+ return;
1315
+ }
1316
+ throw new Error(`Unsupported mutation ${JSON.stringify(mutation, null, 2)}`);
1317
+ }), typeof this.params.timestamp == "string" && operations.push((doc) => doc ? Object.assign(doc, { _updatedAt: this.params.timestamp }) : null);
1318
+ const prevRev = this.previousRev, rev = this.resultRev || this.transactionId;
1319
+ this.compiled = (doc) => {
1320
+ if (prevRev && doc && prevRev !== doc._rev)
1321
+ throw new Error(
1322
+ `Previous revision for this mutation was ${prevRev}, but the document revision is ${doc._rev}`
1323
+ );
1324
+ let result = doc;
1325
+ for (const operation of operations)
1326
+ result = operation(result);
1327
+ return result && rev && (result === doc && (result = Object.assign({}, doc)), result._rev = rev), result;
1328
+ };
1329
+ }
1330
+ apply(document) {
1331
+ debug("Applying mutation %O to document %O", this.mutations, document), this.compiled || this.compile();
1332
+ const result = this.compiled(document);
1333
+ return debug(" => %O", result), result;
1334
+ }
1335
+ static applyAll(document, mutations) {
1336
+ return mutations.reduce((doc, mutation) => mutation.apply(doc), document);
1337
+ }
1338
+ // Given a number of yet-to-be-committed mutation objects, collects them into one big mutation
1339
+ // any metadata like transactionId is ignored and must be submitted by the client. It is assumed
1340
+ // that all mutations are on the same document.
1341
+ // TOOO: Optimize mutations, eliminating mutations that overwrite themselves!
1342
+ static squash(document, mutations) {
1343
+ const squashed = mutations.reduce(
1344
+ (result, mutation) => result.concat(...mutation.mutations),
1345
+ []
1346
+ );
1347
+ return new Mutation({ mutations: squashed });
1348
+ }
1349
+ }
1350
+ class Document {
1351
+ /**
1352
+ * Incoming patches from the server waiting to be applied to HEAD
1353
+ */
1354
+ incoming = [];
1355
+ /**
1356
+ * Patches we know has been subitted to the server, but has not been seen yet in the return channel
1357
+ * so we can't be sure about the ordering yet (someone else might have slipped something between them)
1358
+ */
1359
+ submitted = [];
1360
+ /**
1361
+ * Pending mutations
1362
+ */
1363
+ pending = [];
1364
+ /**
1365
+ * Our model of the document according to the incoming patches from the server
1366
+ */
1367
+ HEAD;
1368
+ /**
1369
+ * Our optimistic model of what the document will probably look like as soon as all our patches
1370
+ * have been processed. Updated every time we stage a new mutation, but also might revert back
1371
+ * to previous states if our mutations fail, or could change if unexpected mutations arrive
1372
+ * between our own. The `onRebase` callback will be called when EDGE changes in this manner.
1373
+ */
1374
+ EDGE;
1375
+ /**
1376
+ * Called with the EDGE document when that document changes for a reason other than us staging
1377
+ * a new patch or receiving a mutation from the server while our EDGE is in sync with HEAD:
1378
+ * I.e. when EDGE changes because the order of mutations has changed in relation to our
1379
+ * optimistic predictions.
1380
+ */
1381
+ onRebase;
1382
+ /**
1383
+ * Called when we receive a patch in the normal order of things, but the mutation is not ours
1384
+ */
1385
+ onMutation;
1386
+ /**
1387
+ * Called when consistency state changes with the boolean value of the current consistency state
1388
+ */
1389
+ onConsistencyChanged;
1390
+ /**
1391
+ * Called whenever a new incoming mutation comes in. These are always ordered correctly.
1392
+ */
1393
+ onRemoteMutation;
1394
+ /**
1395
+ * We are consistent when there are no unresolved mutations of our own, and no un-applicable
1396
+ * incoming mutations. When this has been going on for too long, and there has been a while
1397
+ * since we staged a new mutation, it is time to reset your state.
1398
+ */
1399
+ inconsistentAt = null;
1400
+ /**
1401
+ * The last time we staged a patch of our own. If we have been inconsistent for a while, but it
1402
+ * hasn't been long since we staged a new mutation, the reason is probably just because the user
1403
+ * is typing or something.
1404
+ *
1405
+ * Should be used as a guard against resetting state for inconsistency reasons.
1406
+ */
1407
+ lastStagedAt = null;
1408
+ constructor(doc) {
1409
+ this.reset(doc), this.HEAD = doc, this.EDGE = doc;
1410
+ }
1411
+ // Reset the state of the Document, used to recover from unsavory states by reloading the document
1412
+ reset(doc) {
1413
+ this.incoming = [], this.submitted = [], this.pending = [], this.inconsistentAt = null, this.HEAD = doc, this.EDGE = doc, this.considerIncoming(), this.updateConsistencyFlag();
1414
+ }
1415
+ // Call when a mutation arrives from Sanity
1416
+ arrive(mutation) {
1417
+ this.incoming.push(mutation), this.considerIncoming(), this.updateConsistencyFlag();
1418
+ }
1419
+ // Call to signal that we are submitting a mutation. Returns a callback object with a
1420
+ // success and failure handler that must be called according to the outcome of our
1421
+ // submission.
1422
+ stage(mutation, silent) {
1423
+ if (!mutation.transactionId)
1424
+ throw new Error("Mutations _must_ have transactionId when submitted");
1425
+ this.lastStagedAt = /* @__PURE__ */ new Date(), debug("Staging mutation %s (pushed to pending)", mutation.transactionId), this.pending.push(mutation), this.EDGE = mutation.apply(this.EDGE), this.onMutation && !silent && this.onMutation({
1426
+ mutation,
1427
+ document: this.EDGE,
1428
+ remote: !1
1429
+ });
1430
+ const txnId = mutation.transactionId;
1431
+ return this.updateConsistencyFlag(), {
1432
+ success: () => {
1433
+ this.pendingSuccessfullySubmitted(txnId), this.updateConsistencyFlag();
1434
+ },
1435
+ failure: () => {
1436
+ this.pendingFailed(txnId), this.updateConsistencyFlag();
1437
+ }
1438
+ };
1439
+ }
1440
+ // Call to check if everything is nice and quiet and there are no unresolved mutations.
1441
+ // Means this model thinks both HEAD and EDGE is up to date with what the server sees.
1442
+ isConsistent() {
1443
+ return !this.inconsistentAt;
1444
+ }
1445
+ // Private
1446
+ // Attempts to apply any resolvable incoming patches to HEAD. Will keep patching as long as there
1447
+ // are applicable patches to be applied
1448
+ considerIncoming() {
1449
+ let mustRebase = !1, nextMut;
1450
+ const rebaseMutations = [];
1451
+ if (this.HEAD && this.HEAD._updatedAt) {
1452
+ const updatedAt = new Date(this.HEAD._updatedAt);
1453
+ this.incoming.find((mut) => mut.timestamp && mut.timestamp < updatedAt) && (this.incoming = this.incoming.filter((mut) => mut.timestamp && mut.timestamp < updatedAt));
1454
+ }
1455
+ let protect = 0;
1456
+ do {
1457
+ if (this.HEAD) {
1458
+ const HEAD = this.HEAD;
1459
+ nextMut = HEAD._rev ? this.incoming.find((mut) => mut.previousRev === HEAD._rev) : void 0;
1460
+ } else
1461
+ nextMut = this.incoming.find((mut) => mut.appliesToMissingDocument());
1462
+ if (nextMut) {
1463
+ const applied = this.applyIncoming(nextMut);
1464
+ if (mustRebase = mustRebase || applied, mustRebase && rebaseMutations.push(nextMut), protect++ > 10)
1465
+ throw new Error(
1466
+ `Mutator stuck flushing incoming mutations. Probably stuck here: ${JSON.stringify(
1467
+ nextMut
1468
+ )}`
1469
+ );
1470
+ }
1471
+ } while (nextMut);
1472
+ this.incoming.length > 0 && debug.enabled && debug(
1473
+ "Unable to apply mutations %s",
1474
+ this.incoming.map((mut) => mut.transactionId).join(", ")
1475
+ ), mustRebase && this.rebase(rebaseMutations);
1476
+ }
1477
+ // check current consistency state, update flag and invoke callback if needed
1478
+ updateConsistencyFlag() {
1479
+ const wasConsistent = this.isConsistent(), isConsistent = this.pending.length === 0 && this.submitted.length === 0 && this.incoming.length === 0;
1480
+ isConsistent ? this.inconsistentAt = null : this.inconsistentAt || (this.inconsistentAt = /* @__PURE__ */ new Date()), wasConsistent != isConsistent && this.onConsistencyChanged && (debug(isConsistent ? "Buffered document is inconsistent" : "Buffered document is consistent"), this.onConsistencyChanged(isConsistent));
1481
+ }
1482
+ // apply an incoming patch that has been prequalified as the next in line for this document
1483
+ applyIncoming(mut) {
1484
+ if (!mut)
1485
+ return !1;
1486
+ if (!mut.transactionId)
1487
+ throw new Error("Received incoming mutation without a transaction ID");
1488
+ if (debug(
1489
+ "Applying mutation %s -> %s to rev %s",
1490
+ mut.previousRev,
1491
+ mut.resultRev,
1492
+ this.HEAD && this.HEAD._rev
1493
+ ), this.HEAD = mut.apply(this.HEAD), this.onRemoteMutation && this.onRemoteMutation(mut), this.incoming = this.incoming.filter((m) => m.transactionId !== mut.transactionId), this.hasUnresolvedMutations()) {
1494
+ const needRebase = this.consumeUnresolved(mut.transactionId);
1495
+ return debug.enabled && (debug(
1496
+ `Incoming mutation ${mut.transactionId} appeared while there were pending or submitted local mutations`
1497
+ ), debug(`Submitted txnIds: ${this.submitted.map((m) => m.transactionId).join(", ")}`), debug(`Pending txnIds: ${this.pending.map((m) => m.transactionId).join(", ")}`), debug("needRebase === %s", needRebase)), needRebase;
1498
+ }
1499
+ return debug(
1500
+ "Remote mutation %s arrived w/o any pending or submitted local mutations",
1501
+ mut.transactionId
1502
+ ), this.EDGE = this.HEAD, this.onMutation && this.onMutation({
1503
+ mutation: mut,
1504
+ document: this.EDGE,
1505
+ remote: !0
1506
+ }), !1;
1507
+ }
1508
+ /**
1509
+ * Returns true if there are unresolved mutations between HEAD and EDGE, meaning we have
1510
+ * mutations that are still waiting to be either submitted, or to be confirmed by the server.
1511
+ *
1512
+ * @returns true if there are unresolved mutations between HEAD and EDGE, false otherwise
1513
+ */
1514
+ hasUnresolvedMutations() {
1515
+ return this.submitted.length > 0 || this.pending.length > 0;
1516
+ }
1517
+ /**
1518
+ * When an incoming mutation is applied to HEAD, this is called to remove the mutation from
1519
+ * the unresolved state. If the newly applied patch is the next upcoming unresolved mutation,
1520
+ * no rebase is needed, but we might have the wrong idea about the ordering of mutations, so in
1521
+ * that case we are given the flag `needRebase` to tell us that this mutation arrived out of
1522
+ * order in terms of our optimistic version, so a rebase is needed.
1523
+ *
1524
+ * @param txnId - Transaction ID of the remote mutation
1525
+ * @returns true if rebase is needed, false otherwise
1526
+ */
1527
+ consumeUnresolved(txnId) {
1528
+ if (this.submitted.length === 0 && this.pending.length === 0)
1529
+ return !1;
1530
+ if (this.submitted.length !== 0) {
1531
+ if (this.submitted[0].transactionId === txnId)
1532
+ return debug(
1533
+ "Remote mutation %s matches upcoming submitted mutation, consumed from 'submitted' buffer",
1534
+ txnId
1535
+ ), this.submitted.shift(), !1;
1536
+ } else if (this.pending.length > 0 && this.pending[0].transactionId === txnId)
1537
+ return debug(
1538
+ "Remote mutation %s matches upcoming pending mutation, consumed from 'pending' buffer",
1539
+ txnId
1540
+ ), this.pending.shift(), !1;
1541
+ return debug(
1542
+ "The mutation was not the upcoming mutation, scrubbing. Pending: %d, Submitted: %d",
1543
+ this.pending.length,
1544
+ this.submitted.length
1545
+ ), this.submitted = this.submitted.filter((mut) => mut.transactionId !== txnId), this.pending = this.pending.filter((mut) => mut.transactionId !== txnId), debug("After scrubbing: Pending: %d, Submitted: %d", this.pending.length, this.submitted.length), !0;
1546
+ }
1547
+ pendingSuccessfullySubmitted(pendingTxnId) {
1548
+ if (this.pending.length === 0)
1549
+ return;
1550
+ const first = this.pending[0];
1551
+ if (first.transactionId === pendingTxnId) {
1552
+ this.pending.shift(), this.submitted.push(first);
1553
+ return;
1554
+ }
1555
+ let justSubmitted;
1556
+ const stillPending = [];
1557
+ this.pending.forEach((mutation) => {
1558
+ if (mutation.transactionId === pendingTxnId) {
1559
+ justSubmitted = mutation;
1560
+ return;
1561
+ }
1562
+ stillPending.push(mutation);
1563
+ }), justSubmitted && this.submitted.push(justSubmitted), this.pending = stillPending, this.rebase([]);
1564
+ }
1565
+ pendingFailed(pendingTxnId) {
1566
+ this.pending = this.pending.filter((mutation) => mutation.transactionId !== pendingTxnId), this.rebase([]);
1567
+ }
1568
+ rebase(incomingMutations) {
1569
+ const oldEdge = this.EDGE;
1570
+ this.EDGE = Mutation.applyAll(this.HEAD, this.submitted.concat(this.pending)), oldEdge !== null && this.EDGE !== null && (oldEdge._rev = this.EDGE._rev), !isEqual(this.EDGE, oldEdge) && this.onRebase && this.onRebase(this.EDGE, incomingMutations, this.pending);
1571
+ }
1572
+ }
1573
+ class SquashingBuffer {
1574
+ /**
1575
+ * The document forming the basis of this squash
1576
+ */
1577
+ BASIS;
1578
+ /**
1579
+ * The document after the out-Mutation has been applied, but before the staged
1580
+ * operations are committed.
1581
+ */
1582
+ PRESTAGE;
1583
+ /**
1584
+ * setOperations contain the latest set operation by path. If the set-operations are
1585
+ * updating strings to new strings, they are rewritten as diffMatchPatch operations,
1586
+ * any new set operations on the same paths overwrites any older set operations.
1587
+ * Only set-operations assigning plain values to plain values gets optimized like this.
1588
+ */
1589
+ setOperations;
1590
+ /**
1591
+ * `documentPresent` is true whenever we know that the document must be present due
1592
+ * to preceeding mutations. `false` implies that it may or may not already exist.
1593
+ */
1594
+ documentPresent;
1595
+ /**
1596
+ * The operations in the out-Mutation are not able to be optimized any further
1597
+ */
1598
+ out = [];
1599
+ /**
1600
+ * Staged mutation operations
1601
+ */
1602
+ staged;
1603
+ constructor(doc) {
1604
+ doc ? debug("Reset mutation buffer to rev %s", doc._rev) : debug("Reset mutation buffer state to document being deleted"), this.staged = [], this.setOperations = {}, this.documentPresent = !1, this.BASIS = doc, this.PRESTAGE = doc;
1605
+ }
1606
+ add(mut) {
1607
+ mut.mutations.forEach((op) => this.addOperation(op));
1608
+ }
1609
+ hasChanges() {
1610
+ return this.out.length > 0 || Object.keys(this.setOperations).length > 0;
1611
+ }
1612
+ /**
1613
+ * Extracts the mutations in this buffer.
1614
+ * After this is done, the buffer lifecycle is over and the client should
1615
+ * create an new one with the new, updated BASIS.
1616
+ *
1617
+ * @param txnId - Transaction ID
1618
+ * @returns A `Mutation` instance if we had outgoing mutations pending, null otherwise
1619
+ */
1620
+ purge(txnId) {
1621
+ this.stashStagedOperations();
1622
+ let result = null;
1623
+ return this.out.length > 0 && (debug("Purged mutation buffer"), result = new Mutation({
1624
+ mutations: this.out,
1625
+ resultRev: txnId,
1626
+ transactionId: txnId
1627
+ })), this.out = [], this.documentPresent = !1, result;
1628
+ }
1629
+ addOperation(op) {
1630
+ if (op.patch && op.patch.set && "id" in op.patch && op.patch.id === this.PRESTAGE?._id && Object.keys(op.patch).length === 2) {
1631
+ const setPatch = op.patch.set, unoptimizable = {};
1632
+ for (const path of Object.keys(setPatch))
1633
+ setPatch.hasOwnProperty(path) && (this.optimiseSetOperation(path, setPatch[path]) || (unoptimizable[path] = setPatch[path]));
1634
+ Object.keys(unoptimizable).length > 0 && (debug("Unoptimizable set-operation detected, purging optimization buffer"), this.staged.push({ patch: { id: this.PRESTAGE._id, set: unoptimizable } }), this.stashStagedOperations());
1635
+ return;
1636
+ }
1637
+ if (op.createIfNotExists && this.PRESTAGE && op.createIfNotExists._id === this.PRESTAGE._id) {
1638
+ this.documentPresent || (this.staged.push(op), this.documentPresent = !0, this.stashStagedOperations());
1639
+ return;
1640
+ }
1641
+ debug("Unoptimizable mutation detected, purging optimization buffer"), this.staged.push(op), this.stashStagedOperations();
1642
+ }
1643
+ /**
1644
+ * Attempt to perform one single set operation in an optimised manner, return value
1645
+ * reflects whether or not the operation could be performed.
1646
+
1647
+ * @param path - The JSONPath to the set operation in question
1648
+ * @param nextValue - The value to be set
1649
+ * @returns True of optimized, false otherwise
1650
+ */
1651
+ optimiseSetOperation(path, nextValue) {
1652
+ if (typeof nextValue == "object")
1653
+ return !1;
1654
+ const matches = extractWithPath(path, this.PRESTAGE);
1655
+ if (matches.length !== 1)
1656
+ return !1;
1657
+ const match = matches[0];
1658
+ if (typeof match.value == "object" || !this.PRESTAGE)
1659
+ return !1;
1660
+ let op = null;
1661
+ if (match.value === nextValue)
1662
+ op = null;
1663
+ else if (typeof match.value == "string" && typeof nextValue == "string")
1664
+ try {
1665
+ const patch = stringifyPatches(makePatches(match.value, nextValue));
1666
+ op = { patch: { id: this.PRESTAGE._id, diffMatchPatch: { [path]: patch } } };
1667
+ } catch {
1668
+ return !1;
1669
+ }
1670
+ else
1671
+ op = { patch: { id: this.PRESTAGE._id, set: { [path]: nextValue } } };
1672
+ const canonicalPath = arrayToJSONMatchPath(match.path);
1673
+ return op ? this.setOperations[canonicalPath] = op : delete this.setOperations[canonicalPath], !0;
1674
+ }
1675
+ stashStagedOperations() {
1676
+ const nextOps = [];
1677
+ Object.keys(this.setOperations).forEach((key) => {
1678
+ const op = this.setOperations[key];
1679
+ op && nextOps.push(op);
1680
+ }), nextOps.push(...this.staged), nextOps.length > 0 && (this.PRESTAGE = new Mutation({ mutations: nextOps }).apply(this.PRESTAGE), this.staged = [], this.setOperations = {}), this.out.push(...nextOps);
1681
+ }
1682
+ /**
1683
+ * Rebases given the new base-document
1684
+ *
1685
+ * @param newBasis - New base document to rebase on
1686
+ * @returns New "edge" document with buffered changes integrated
1687
+ */
1688
+ rebase(newBasis) {
1689
+ return this.stashStagedOperations(), newBasis === null ? (this.out = [], this.BASIS = newBasis, this.PRESTAGE = newBasis, this.documentPresent = !1) : (this.BASIS = newBasis, this.out ? this.PRESTAGE = new Mutation({ mutations: this.out }).apply(this.BASIS) : this.PRESTAGE = this.BASIS), this.PRESTAGE;
1690
+ }
1691
+ }
1692
+ const ONE_MINUTE = 1e3 * 60;
1693
+ class Commit {
1694
+ mutations;
1695
+ tries;
1696
+ resolve;
1697
+ reject;
1698
+ constructor(mutations, { resolve, reject }) {
1699
+ this.mutations = mutations, this.tries = 0, this.resolve = resolve, this.reject = reject;
1700
+ }
1701
+ apply(doc) {
1702
+ return Mutation.applyAll(doc, this.mutations);
1703
+ }
1704
+ squash(doc) {
1705
+ const result = Mutation.squash(doc, this.mutations);
1706
+ return result.assignRandomTransactionId(), result;
1707
+ }
1708
+ }
1583
1709
  const mutReducerFn = (acc, mut) => acc.concat(mut.mutations);
1584
- /**
1585
- * @internal
1586
- */
1587
- var BufferedDocument = class {
1588
- mutations;
1589
- /**
1590
- * The Document we are wrapping
1591
- */
1592
- document;
1593
- /**
1594
- * The Document with local changes applied
1595
- */
1596
- LOCAL;
1597
- /**
1598
- * Commits that are waiting to be delivered to the server
1599
- */
1600
- commits;
1601
- /**
1602
- * Local mutations that are not scheduled to be committed yet
1603
- */
1604
- buffer;
1605
- /**
1606
- * Assignable event handler for when the buffered document applies a mutation
1607
- */
1608
- onMutation;
1609
- /**
1610
- * Assignable event handler for when a remote mutation happened
1611
- */
1612
- onRemoteMutation;
1613
- /**
1614
- * Assignable event handler for when the buffered document rebased
1615
- */
1616
- onRebase;
1617
- /**
1618
- * Assignable event handler for when the document is deleted
1619
- */
1620
- onDelete;
1621
- /**
1622
- * Assignable event handler for when the state of consistency changed
1623
- */
1624
- onConsistencyChanged;
1625
- /**
1626
- * Assignable event handler for when the buffered document should commit changes
1627
- */
1628
- commitHandler;
1629
- /**
1630
- * Whether or not we are currently commiting
1631
- */
1632
- committerRunning = !1;
1633
- constructor(doc) {
1634
- this.buffer = new SquashingBuffer(doc), this.document = new Document(doc), this.document.onMutation = (msg) => this.handleDocMutation(msg), this.document.onRemoteMutation = (mut) => this.onRemoteMutation && this.onRemoteMutation(mut), this.document.onRebase = (edge, remoteMutations, localMutations) => this.handleDocRebase(edge, remoteMutations, localMutations), this.document.onConsistencyChanged = (msg) => this.handleDocConsistencyChanged(msg), this.LOCAL = doc, this.mutations = [], this.commits = [];
1635
- }
1636
- reset(doc) {
1637
- doc ? debug("Document state reset to revision %s", doc._rev) : debug("Document state reset to being deleted"), this.document.reset(doc), this.rebase([], []), this.handleDocConsistencyChanged(this.document.isConsistent());
1638
- }
1639
- add(mutation) {
1640
- this.onConsistencyChanged && this.onConsistencyChanged(!1), debug("Staged local mutation"), this.buffer.add(mutation);
1641
- let oldLocal = this.LOCAL;
1642
- this.LOCAL = mutation.apply(this.LOCAL), this.onMutation && oldLocal !== this.LOCAL && (debug("onMutation fired"), this.onMutation({
1643
- mutation,
1644
- document: this.LOCAL,
1645
- remote: !1
1646
- }), this.LOCAL === null && this.onDelete && this.onDelete(this.LOCAL));
1647
- }
1648
- arrive(mutation) {
1649
- if (debug("Remote mutation arrived %s -> %s", mutation.previousRev, mutation.resultRev), mutation.previousRev === mutation.resultRev) throw Error(`Mutation ${mutation.transactionId} has previousRev === resultRev (${mutation.previousRev})`);
1650
- return this.document.arrive(mutation);
1651
- }
1652
- commit() {
1653
- return new Promise((resolve, reject) => {
1654
- if (!this.buffer.hasChanges()) {
1655
- resolve();
1656
- return;
1657
- }
1658
- debug("Committing local changes");
1659
- let pendingMutations = this.buffer.purge();
1660
- this.commits.push(new Commit(pendingMutations ? [pendingMutations] : [], {
1661
- resolve,
1662
- reject
1663
- })), this.buffer = new SquashingBuffer(this.LOCAL), this.performCommits();
1664
- });
1665
- }
1666
- performCommits() {
1667
- if (!this.commitHandler) throw Error("No commitHandler configured for this BufferedDocument");
1668
- this.committerRunning || this._cycleCommitter();
1669
- }
1670
- _cycleCommitter() {
1671
- let commit = this.commits.shift();
1672
- if (!commit) {
1673
- this.committerRunning = !1;
1674
- return;
1675
- }
1676
- this.committerRunning = !0;
1677
- let squashed = commit.squash(this.LOCAL), docResponder = this.document.stage(squashed, !0), responder = {
1678
- success: () => {
1679
- debug("Commit succeeded"), docResponder.success(), commit.resolve(), this._cycleCommitter();
1680
- },
1681
- failure: () => {
1682
- debug("Commit failed"), commit.tries += 1, this.LOCAL !== null && this.commits.unshift(commit), docResponder.failure(), commit.tries < 200 && setTimeout(() => this._cycleCommitter(), Math.min(commit.tries * 1e3, 6e4));
1683
- },
1684
- cancel: (error) => {
1685
- this.commits.forEach((comm) => comm.reject(error)), this.commits = [], this.reset(this.document.HEAD), this.buffer = new SquashingBuffer(this.LOCAL), this.committerRunning = !1;
1686
- }
1687
- };
1688
- debug("Posting commit"), this.commitHandler && this.commitHandler({
1689
- mutation: squashed,
1690
- success: responder.success,
1691
- failure: responder.failure,
1692
- cancel: responder.cancel
1693
- });
1694
- }
1695
- handleDocRebase(edge, remoteMutations, localMutations) {
1696
- this.rebase(remoteMutations, localMutations);
1697
- }
1698
- handleDocumentDeleted() {
1699
- debug("Document deleted"), this.LOCAL !== null && this.onDelete && this.onDelete(this.LOCAL), this.commits = [], this.mutations = [];
1700
- }
1701
- handleDocMutation(msg) {
1702
- if (this.commits.length === 0 && !this.buffer.hasChanges()) {
1703
- debug("Document mutated from remote with no local changes"), this.LOCAL = this.document.EDGE, this.buffer = new SquashingBuffer(this.LOCAL), this.onMutation && this.onMutation(msg);
1704
- return;
1705
- }
1706
- debug("Document mutated from remote with local changes"), this.document.EDGE === null && this.handleDocumentDeleted(), this.rebase([msg.mutation], []);
1707
- }
1708
- rebase(remoteMutations, localMutations) {
1709
- debug("Rebasing document"), this.document.EDGE === null && this.handleDocumentDeleted();
1710
- let oldLocal = this.LOCAL;
1711
- this.LOCAL = this.commits.reduce((doc, commit) => commit.apply(doc), this.document.EDGE), this.LOCAL = this.buffer.rebase(this.LOCAL), oldLocal !== null && this.LOCAL !== null && (oldLocal._rev = this.LOCAL._rev), !isEqual(this.LOCAL, oldLocal) && this.onRebase && this.onRebase(this.LOCAL, remoteMutations.reduce(mutReducerFn, []), localMutations.reduce(mutReducerFn, []));
1712
- }
1713
- handleDocConsistencyChanged(isConsistent) {
1714
- if (!this.onConsistencyChanged) return;
1715
- let hasLocalChanges = this.commits.length > 0 || this.buffer.hasChanges();
1716
- isConsistent && !hasLocalChanges && this.onConsistencyChanged(!0), isConsistent || this.onConsistencyChanged(!1);
1717
- }
1710
+ class BufferedDocument {
1711
+ mutations;
1712
+ /**
1713
+ * The Document we are wrapping
1714
+ */
1715
+ document;
1716
+ /**
1717
+ * The Document with local changes applied
1718
+ */
1719
+ LOCAL;
1720
+ /**
1721
+ * Commits that are waiting to be delivered to the server
1722
+ */
1723
+ commits;
1724
+ /**
1725
+ * Local mutations that are not scheduled to be committed yet
1726
+ */
1727
+ buffer;
1728
+ /**
1729
+ * Assignable event handler for when the buffered document applies a mutation
1730
+ */
1731
+ onMutation;
1732
+ /**
1733
+ * Assignable event handler for when a remote mutation happened
1734
+ */
1735
+ onRemoteMutation;
1736
+ /**
1737
+ * Assignable event handler for when the buffered document rebased
1738
+ */
1739
+ onRebase;
1740
+ /**
1741
+ * Assignable event handler for when the document is deleted
1742
+ */
1743
+ onDelete;
1744
+ /**
1745
+ * Assignable event handler for when the state of consistency changed
1746
+ */
1747
+ onConsistencyChanged;
1748
+ /**
1749
+ * Assignable event handler for when the buffered document should commit changes
1750
+ */
1751
+ commitHandler;
1752
+ /**
1753
+ * Whether or not we are currently commiting
1754
+ */
1755
+ committerRunning = !1;
1756
+ constructor(doc) {
1757
+ this.buffer = new SquashingBuffer(doc), this.document = new Document(doc), this.document.onMutation = (msg) => this.handleDocMutation(msg), this.document.onRemoteMutation = (mut) => this.onRemoteMutation && this.onRemoteMutation(mut), this.document.onRebase = (edge, remoteMutations, localMutations) => this.handleDocRebase(edge, remoteMutations, localMutations), this.document.onConsistencyChanged = (msg) => this.handleDocConsistencyChanged(msg), this.LOCAL = doc, this.mutations = [], this.commits = [];
1758
+ }
1759
+ // Used to reset the state of the local document model. If the model has been inconsistent
1760
+ // for too long, it has probably missed a notification, and should reload the document from the server
1761
+ reset(doc) {
1762
+ doc ? debug("Document state reset to revision %s", doc._rev) : debug("Document state reset to being deleted"), this.document.reset(doc), this.rebase([], []), this.handleDocConsistencyChanged(this.document.isConsistent());
1763
+ }
1764
+ // Add a change to the buffer
1765
+ add(mutation) {
1766
+ this.onConsistencyChanged && this.onConsistencyChanged(!1), debug("Staged local mutation"), this.buffer.add(mutation);
1767
+ const oldLocal = this.LOCAL;
1768
+ this.LOCAL = mutation.apply(this.LOCAL), this.onMutation && oldLocal !== this.LOCAL && (debug("onMutation fired"), this.onMutation({
1769
+ mutation,
1770
+ document: this.LOCAL,
1771
+ remote: !1
1772
+ }), this.LOCAL === null && this.onDelete && this.onDelete(this.LOCAL));
1773
+ }
1774
+ // Call when a mutation arrives from Sanity
1775
+ arrive(mutation) {
1776
+ if (debug("Remote mutation arrived %s -> %s", mutation.previousRev, mutation.resultRev), mutation.previousRev === mutation.resultRev)
1777
+ throw new Error(
1778
+ `Mutation ${mutation.transactionId} has previousRev === resultRev (${mutation.previousRev})`
1779
+ );
1780
+ return this.document.arrive(mutation);
1781
+ }
1782
+ // Submit all mutations in the buffer to be committed
1783
+ commit() {
1784
+ return new Promise((resolve, reject) => {
1785
+ if (!this.buffer.hasChanges()) {
1786
+ resolve();
1787
+ return;
1788
+ }
1789
+ debug("Committing local changes");
1790
+ const pendingMutations = this.buffer.purge();
1791
+ this.commits.push(new Commit(pendingMutations ? [pendingMutations] : [], { resolve, reject })), this.buffer = new SquashingBuffer(this.LOCAL), this.performCommits();
1792
+ });
1793
+ }
1794
+ // Starts the committer that will try to committ all staged commits to the database
1795
+ // by calling the commitHandler. Will keep running until all commits are successfully
1796
+ // committed.
1797
+ performCommits() {
1798
+ if (!this.commitHandler)
1799
+ throw new Error("No commitHandler configured for this BufferedDocument");
1800
+ this.committerRunning || this._cycleCommitter();
1801
+ }
1802
+ // TODO: Error handling, right now retries after every error
1803
+ _cycleCommitter() {
1804
+ const commit = this.commits.shift();
1805
+ if (!commit) {
1806
+ this.committerRunning = !1;
1807
+ return;
1808
+ }
1809
+ this.committerRunning = !0;
1810
+ const squashed = commit.squash(this.LOCAL), docResponder = this.document.stage(squashed, !0), responder = {
1811
+ success: () => {
1812
+ debug("Commit succeeded"), docResponder.success(), commit.resolve(), this._cycleCommitter();
1813
+ },
1814
+ failure: () => {
1815
+ debug("Commit failed"), commit.tries += 1, this.LOCAL !== null && this.commits.unshift(commit), docResponder.failure(), commit.tries < 200 && setTimeout(() => this._cycleCommitter(), Math.min(commit.tries * 1e3, ONE_MINUTE));
1816
+ },
1817
+ cancel: (error) => {
1818
+ this.commits.forEach((comm) => comm.reject(error)), this.commits = [], this.reset(this.document.HEAD), this.buffer = new SquashingBuffer(this.LOCAL), this.committerRunning = !1;
1819
+ }
1820
+ };
1821
+ debug("Posting commit"), this.commitHandler && this.commitHandler({
1822
+ mutation: squashed,
1823
+ success: responder.success,
1824
+ failure: responder.failure,
1825
+ cancel: responder.cancel
1826
+ });
1827
+ }
1828
+ handleDocRebase(edge, remoteMutations, localMutations) {
1829
+ this.rebase(remoteMutations, localMutations);
1830
+ }
1831
+ handleDocumentDeleted() {
1832
+ debug("Document deleted"), this.LOCAL !== null && this.onDelete && this.onDelete(this.LOCAL), this.commits = [], this.mutations = [];
1833
+ }
1834
+ handleDocMutation(msg) {
1835
+ if (this.commits.length === 0 && !this.buffer.hasChanges()) {
1836
+ debug("Document mutated from remote with no local changes"), this.LOCAL = this.document.EDGE, this.buffer = new SquashingBuffer(this.LOCAL), this.onMutation && this.onMutation(msg);
1837
+ return;
1838
+ }
1839
+ debug("Document mutated from remote with local changes"), this.document.EDGE === null && this.handleDocumentDeleted(), this.rebase([msg.mutation], []);
1840
+ }
1841
+ rebase(remoteMutations, localMutations) {
1842
+ debug("Rebasing document"), this.document.EDGE === null && this.handleDocumentDeleted();
1843
+ const oldLocal = this.LOCAL;
1844
+ this.LOCAL = this.commits.reduce((doc, commit) => commit.apply(doc), this.document.EDGE), this.LOCAL = this.buffer.rebase(this.LOCAL), oldLocal !== null && this.LOCAL !== null && (oldLocal._rev = this.LOCAL._rev), !isEqual(this.LOCAL, oldLocal) && this.onRebase && this.onRebase(
1845
+ this.LOCAL,
1846
+ remoteMutations.reduce(mutReducerFn, []),
1847
+ localMutations.reduce(mutReducerFn, [])
1848
+ );
1849
+ }
1850
+ handleDocConsistencyChanged(isConsistent) {
1851
+ if (!this.onConsistencyChanged)
1852
+ return;
1853
+ const hasLocalChanges = this.commits.length > 0 || this.buffer.hasChanges();
1854
+ isConsistent && !hasLocalChanges && this.onConsistencyChanged(!0), isConsistent || this.onConsistencyChanged(!1);
1855
+ }
1856
+ }
1857
+ export {
1858
+ BufferedDocument,
1859
+ Mutation,
1860
+ arrayToJSONMatchPath,
1861
+ extract,
1862
+ extractWithPath
1718
1863
  };
1719
- export { BufferedDocument, Mutation, arrayToJSONMatchPath, extract, extractWithPath };
1720
-
1721
- //# sourceMappingURL=index.js.map
1864
+ //# sourceMappingURL=index.js.map