UNPKG

28.7 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3 typeof define === 'function' && define.amd ? define(['exports'], factory) :
4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.rfc6902 = {}));
5})(this, (function (exports) { 'use strict';
6
7 /**
8 Unescape token part of a JSON Pointer string
9
10 `token` should *not* contain any '/' characters.
11
12 > Evaluation of each reference token begins by decoding any escaped
13 > character sequence. This is performed by first transforming any
14 > occurrence of the sequence '~1' to '/', and then transforming any
15 > occurrence of the sequence '~0' to '~'. By performing the
16 > substitutions in this order, an implementation avoids the error of
17 > turning '~01' first into '~1' and then into '/', which would be
18 > incorrect (the string '~01' correctly becomes '~1' after
19 > transformation).
20
21 Here's my take:
22
23 ~1 is unescaped with higher priority than ~0 because it is a lower-order escape character.
24 I say "lower order" because '/' needs escaping due to the JSON Pointer serialization technique.
25 Whereas, '~' is escaped because escaping '/' uses the '~' character.
26 */
27 function unescape(token) {
28 return token.replace(/~1/g, '/').replace(/~0/g, '~');
29 }
30 /** Escape token part of a JSON Pointer string
31
32 > '~' needs to be encoded as '~0' and '/'
33 > needs to be encoded as '~1' when these characters appear in a
34 > reference token.
35
36 This is the exact inverse of `unescape()`, so the reverse replacements must take place in reverse order.
37 */
38 function escape(token) {
39 return token.replace(/~/g, '~0').replace(/\//g, '~1');
40 }
41 /**
42 JSON Pointer representation
43 */
44 class Pointer {
45 constructor(tokens = ['']) {
46 this.tokens = tokens;
47 }
48 /**
49 `path` *must* be a properly escaped string.
50 */
51 static fromJSON(path) {
52 const tokens = path.split('/').map(unescape);
53 if (tokens[0] !== '')
54 throw new Error(`Invalid JSON Pointer: ${path}`);
55 return new Pointer(tokens);
56 }
57 toString() {
58 return this.tokens.map(escape).join('/');
59 }
60 /**
61 Returns an object with 'parent', 'key', and 'value' properties.
62 In the special case that this Pointer's path == "",
63 this object will be {parent: null, key: '', value: object}.
64 Otherwise, parent and key will have the property such that parent[key] == value.
65 */
66 evaluate(object) {
67 let parent = null;
68 let key = '';
69 let value = object;
70 for (let i = 1, l = this.tokens.length; i < l; i++) {
71 parent = value;
72 key = this.tokens[i];
73 if (key == '__proto__' || key == 'constructor' || key == 'prototype') {
74 continue;
75 }
76 // not sure if this the best way to handle non-existant paths...
77 value = (parent || {})[key];
78 }
79 return { parent, key, value };
80 }
81 get(object) {
82 return this.evaluate(object).value;
83 }
84 set(object, value) {
85 const endpoint = this.evaluate(object);
86 if (endpoint.parent) {
87 endpoint.parent[endpoint.key] = value;
88 }
89 }
90 push(token) {
91 // mutable
92 this.tokens.push(token);
93 }
94 /**
95 `token` should be a String. It'll be coerced to one anyway.
96
97 immutable (shallowly)
98 */
99 add(token) {
100 const tokens = this.tokens.concat(String(token));
101 return new Pointer(tokens);
102 }
103 }
104
105 const hasOwnProperty = Object.prototype.hasOwnProperty;
106 function objectType(object) {
107 if (object === undefined) {
108 return 'undefined';
109 }
110 if (object === null) {
111 return 'null';
112 }
113 if (Array.isArray(object)) {
114 return 'array';
115 }
116 return typeof object;
117 }
118 function isNonPrimitive(value) {
119 // loose-equality checking for null is faster than strict checking for each of null/undefined/true/false
120 // checking null first, then calling typeof, is faster than vice-versa
121 return value != null && typeof value == 'object';
122 }
123 /**
124 Recursively copy a value.
125
126 @param source - should be a JavaScript primitive, Array, Date, or (plain old) Object.
127 @returns copy of source where every Array and Object have been recursively
128 reconstructed from their constituent elements
129 */
130 function clone(source) {
131 if (!isNonPrimitive(source)) {
132 // short-circuiting is faster than a single return
133 return source;
134 }
135 // x.constructor == Array is the fastest way to check if x is an Array
136 if (source.constructor == Array) {
137 // construction via imperative for-loop is faster than source.map(arrayVsObject)
138 const length = source.length;
139 // setting the Array length during construction is faster than just `[]` or `new Array()`
140 const arrayTarget = new Array(length);
141 for (let i = 0; i < length; i++) {
142 arrayTarget[i] = clone(source[i]);
143 }
144 return arrayTarget;
145 }
146 // Date
147 if (source.constructor == Date) {
148 const dateTarget = new Date(+source);
149 return dateTarget;
150 }
151 // Object
152 const objectTarget = {};
153 // declaring the variable (with const) inside the loop is faster
154 for (const key in source) {
155 // hasOwnProperty costs a bit of performance, but it's semantically necessary
156 // using a global helper is MUCH faster than calling source.hasOwnProperty(key)
157 if (hasOwnProperty.call(source, key)) {
158 objectTarget[key] = clone(source[key]);
159 }
160 }
161 return objectTarget;
162 }
163
164 function isDestructive({ op }) {
165 return op === 'remove' || op === 'replace' || op === 'copy' || op === 'move';
166 }
167 /**
168 List the keys in `minuend` that are not in `subtrahend`.
169
170 A key is only considered if it is both 1) an own-property (o.hasOwnProperty(k))
171 of the object, and 2) has a value that is not undefined. This is to match JSON
172 semantics, where JSON object serialization drops keys with undefined values.
173
174 @param minuend Object of interest
175 @param subtrahend Object of comparison
176 @returns Array of keys that are in `minuend` but not in `subtrahend`.
177 */
178 function subtract(minuend, subtrahend) {
179 // initialize empty object; we only care about the keys, the values can be anything
180 const obj = {};
181 // build up obj with all the properties of minuend
182 for (const add_key in minuend) {
183 if (hasOwnProperty.call(minuend, add_key) && minuend[add_key] !== undefined) {
184 obj[add_key] = 1;
185 }
186 }
187 // now delete all the properties of subtrahend from obj
188 // (deleting a missing key has no effect)
189 for (const del_key in subtrahend) {
190 if (hasOwnProperty.call(subtrahend, del_key) && subtrahend[del_key] !== undefined) {
191 delete obj[del_key];
192 }
193 }
194 // finally, extract whatever keys remain in obj
195 return Object.keys(obj);
196 }
197 /**
198 List the keys that shared by all `objects`.
199
200 The semantics of what constitutes a "key" is described in {@link subtract}.
201
202 @param objects Array of objects to compare
203 @returns Array of keys that are in ("own-properties" of) every object in `objects`.
204 */
205 function intersection(objects) {
206 const length = objects.length;
207 // prepare empty counter to keep track of how many objects each key occurred in
208 const counter = {};
209 // go through each object and increment the counter for each key in that object
210 for (let i = 0; i < length; i++) {
211 const object = objects[i];
212 for (const key in object) {
213 if (hasOwnProperty.call(object, key) && object[key] !== undefined) {
214 counter[key] = (counter[key] || 0) + 1;
215 }
216 }
217 }
218 // now delete all keys from the counter that were not seen in every object
219 for (const key in counter) {
220 if (counter[key] < length) {
221 delete counter[key];
222 }
223 }
224 // finally, extract whatever keys remain in the counter
225 return Object.keys(counter);
226 }
227 function isArrayAdd(array_operation) {
228 return array_operation.op === 'add';
229 }
230 function isArrayRemove(array_operation) {
231 return array_operation.op === 'remove';
232 }
233 function appendArrayOperation(base, operation) {
234 return {
235 // the new operation must be pushed on the end
236 operations: base.operations.concat(operation),
237 cost: base.cost + 1,
238 };
239 }
240 /**
241 Calculate the shortest sequence of operations to get from `input` to `output`,
242 using a dynamic programming implementation of the Levenshtein distance algorithm.
243
244 To get from the input ABC to the output AZ we could just delete all the input
245 and say "insert A, insert Z" and be done with it. That's what we do if the
246 input is empty. But we can be smarter.
247
248 output
249 A Z
250 - -
251 [0] 1 2
252 input A | 1 [0] 1
253 B | 2 [1] 1
254 C | 3 2 [2]
255
256 1) start at 0,0 (+0)
257 2) keep A (+0)
258 3) remove B (+1)
259 4) replace C with Z (+1)
260
261 If the `input` (source) is empty, they'll all be in the top row, resulting in an
262 array of 'add' operations.
263 If the `output` (target) is empty, everything will be in the left column,
264 resulting in an array of 'remove' operations.
265
266 @returns A list of add/remove/replace operations.
267 */
268 function diffArrays(input, output, ptr, diff = diffAny) {
269 // set up cost matrix (very simple initialization: just a map)
270 const memo = {
271 '0,0': { operations: [], cost: 0 },
272 };
273 /**
274 Calculate the cheapest sequence of operations required to get from
275 input.slice(0, i) to output.slice(0, j).
276 There may be other valid sequences with the same cost, but none cheaper.
277
278 @param i The row in the layout above
279 @param j The column in the layout above
280 @returns An object containing a list of operations, along with the total cost
281 of applying them (+1 for each add/remove/replace operation)
282 */
283 function dist(i, j) {
284 // memoized
285 const memo_key = `${i},${j}`;
286 let memoized = memo[memo_key];
287 if (memoized === undefined) {
288 // TODO: this !diff(...).length usage could/should be lazy
289 if (i > 0 && j > 0 && !diff(input[i - 1], output[j - 1], ptr.add(String(i - 1))).length) {
290 // equal (no operations => no cost)
291 memoized = dist(i - 1, j - 1);
292 }
293 else {
294 const alternatives = [];
295 if (i > 0) {
296 // NOT topmost row
297 const remove_base = dist(i - 1, j);
298 const remove_operation = {
299 op: 'remove',
300 index: i - 1,
301 };
302 alternatives.push(appendArrayOperation(remove_base, remove_operation));
303 }
304 if (j > 0) {
305 // NOT leftmost column
306 const add_base = dist(i, j - 1);
307 const add_operation = {
308 op: 'add',
309 index: i - 1,
310 value: output[j - 1],
311 };
312 alternatives.push(appendArrayOperation(add_base, add_operation));
313 }
314 if (i > 0 && j > 0) {
315 // TABLE MIDDLE
316 // supposing we replaced it, compute the rest of the costs:
317 const replace_base = dist(i - 1, j - 1);
318 // okay, the general plan is to replace it, but we can be smarter,
319 // recursing into the structure and replacing only part of it if
320 // possible, but to do so we'll need the original value
321 const replace_operation = {
322 op: 'replace',
323 index: i - 1,
324 original: input[i - 1],
325 value: output[j - 1],
326 };
327 alternatives.push(appendArrayOperation(replace_base, replace_operation));
328 }
329 // the only other case, i === 0 && j === 0, has already been memoized
330 // the meat of the algorithm:
331 // sort by cost to find the lowest one (might be several ties for lowest)
332 // [4, 6, 7, 1, 2].sort((a, b) => a - b) -> [ 1, 2, 4, 6, 7 ]
333 const best = alternatives.sort((a, b) => a.cost - b.cost)[0];
334 memoized = best;
335 }
336 memo[memo_key] = memoized;
337 }
338 return memoized;
339 }
340 // handle weird objects masquerading as Arrays that don't have proper length
341 // properties by using 0 for everything but positive numbers
342 const input_length = (isNaN(input.length) || input.length <= 0) ? 0 : input.length;
343 const output_length = (isNaN(output.length) || output.length <= 0) ? 0 : output.length;
344 const array_operations = dist(input_length, output_length).operations;
345 const [padded_operations] = array_operations.reduce(([operations, padding], array_operation) => {
346 if (isArrayAdd(array_operation)) {
347 const padded_index = array_operation.index + 1 + padding;
348 const index_token = padded_index < (input_length + padding) ? String(padded_index) : '-';
349 const operation = {
350 op: array_operation.op,
351 path: ptr.add(index_token).toString(),
352 value: array_operation.value,
353 };
354 // padding++ // maybe only if array_operation.index > -1 ?
355 return [operations.concat(operation), padding + 1];
356 }
357 else if (isArrayRemove(array_operation)) {
358 const operation = {
359 op: array_operation.op,
360 path: ptr.add(String(array_operation.index + padding)).toString(),
361 };
362 // padding--
363 return [operations.concat(operation), padding - 1];
364 }
365 else { // replace
366 const replace_ptr = ptr.add(String(array_operation.index + padding));
367 const replace_operations = diff(array_operation.original, array_operation.value, replace_ptr);
368 return [operations.concat(...replace_operations), padding];
369 }
370 }, [[], 0]);
371 return padded_operations;
372 }
373 function diffObjects(input, output, ptr, diff = diffAny) {
374 // if a key is in input but not output -> remove it
375 const operations = [];
376 subtract(input, output).forEach(key => {
377 operations.push({ op: 'remove', path: ptr.add(key).toString() });
378 });
379 // if a key is in output but not input -> add it
380 subtract(output, input).forEach(key => {
381 operations.push({ op: 'add', path: ptr.add(key).toString(), value: output[key] });
382 });
383 // if a key is in both, diff it recursively
384 intersection([input, output]).forEach(key => {
385 operations.push(...diff(input[key], output[key], ptr.add(key)));
386 });
387 return operations;
388 }
389 /**
390 `diffAny()` returns an empty array if `input` and `output` are materially equal
391 (i.e., would produce equivalent JSON); otherwise it produces an array of patches
392 that would transform `input` into `output`.
393
394 > Here, "equal" means that the value at the target location and the
395 > value conveyed by "value" are of the same JSON type, and that they
396 > are considered equal by the following rules for that type:
397 > o strings: are considered equal if they contain the same number of
398 > Unicode characters and their code points are byte-by-byte equal.
399 > o numbers: are considered equal if their values are numerically
400 > equal.
401 > o arrays: are considered equal if they contain the same number of
402 > values, and if each value can be considered equal to the value at
403 > the corresponding position in the other array, using this list of
404 > type-specific rules.
405 > o objects: are considered equal if they contain the same number of
406 > members, and if each member can be considered equal to a member in
407 > the other object, by comparing their keys (as strings) and their
408 > values (using this list of type-specific rules).
409 > o literals (false, true, and null): are considered equal if they are
410 > the same.
411 */
412 function diffAny(input, output, ptr, diff = diffAny) {
413 // strict equality handles literals, numbers, and strings (a sufficient but not necessary cause)
414 if (input === output) {
415 return [];
416 }
417 const input_type = objectType(input);
418 const output_type = objectType(output);
419 if (input_type == 'array' && output_type == 'array') {
420 return diffArrays(input, output, ptr, diff);
421 }
422 if (input_type == 'object' && output_type == 'object') {
423 return diffObjects(input, output, ptr, diff);
424 }
425 // at this point we know that input and output are materially different;
426 // could be array -> object, object -> array, boolean -> undefined,
427 // number -> string, or some other combination, but nothing that can be split
428 // up into multiple patches: so `output` must replace `input` wholesale.
429 return [{ op: 'replace', path: ptr.toString(), value: output }];
430 }
431
432 class MissingError extends Error {
433 constructor(path) {
434 super(`Value required at path: ${path}`);
435 this.path = path;
436 this.name = 'MissingError';
437 }
438 }
439 class TestError extends Error {
440 constructor(actual, expected) {
441 super(`Test failed: ${actual} != ${expected}`);
442 this.actual = actual;
443 this.expected = expected;
444 this.name = 'TestError';
445 }
446 }
447 function _add(object, key, value) {
448 if (Array.isArray(object)) {
449 // `key` must be an index
450 if (key == '-') {
451 object.push(value);
452 }
453 else {
454 const index = parseInt(key, 10);
455 object.splice(index, 0, value);
456 }
457 }
458 else {
459 object[key] = value;
460 }
461 }
462 function _remove(object, key) {
463 if (Array.isArray(object)) {
464 // '-' syntax doesn't make sense when removing
465 const index = parseInt(key, 10);
466 object.splice(index, 1);
467 }
468 else {
469 // not sure what the proper behavior is when path = ''
470 delete object[key];
471 }
472 }
473 /**
474 > o If the target location specifies an array index, a new value is
475 > inserted into the array at the specified index.
476 > o If the target location specifies an object member that does not
477 > already exist, a new member is added to the object.
478 > o If the target location specifies an object member that does exist,
479 > that member's value is replaced.
480 */
481 function add(object, operation) {
482 const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
483 // it's not exactly a "MissingError" in the same way that `remove` is -- more like a MissingParent, or something
484 if (endpoint.parent === undefined) {
485 return new MissingError(operation.path);
486 }
487 _add(endpoint.parent, endpoint.key, clone(operation.value));
488 return null;
489 }
490 /**
491 > The "remove" operation removes the value at the target location.
492 > The target location MUST exist for the operation to be successful.
493 */
494 function remove(object, operation) {
495 // endpoint has parent, key, and value properties
496 const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
497 if (endpoint.value === undefined) {
498 return new MissingError(operation.path);
499 }
500 // not sure what the proper behavior is when path = ''
501 _remove(endpoint.parent, endpoint.key);
502 return null;
503 }
504 /**
505 > The "replace" operation replaces the value at the target location
506 > with a new value. The operation object MUST contain a "value" member
507 > whose content specifies the replacement value.
508 > The target location MUST exist for the operation to be successful.
509
510 > This operation is functionally identical to a "remove" operation for
511 > a value, followed immediately by an "add" operation at the same
512 > location with the replacement value.
513
514 Even more simply, it's like the add operation with an existence check.
515 */
516 function replace(object, operation) {
517 const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
518 if (endpoint.parent === null) {
519 return new MissingError(operation.path);
520 }
521 // this existence check treats arrays as a special case
522 if (Array.isArray(endpoint.parent)) {
523 if (parseInt(endpoint.key, 10) >= endpoint.parent.length) {
524 return new MissingError(operation.path);
525 }
526 }
527 else if (endpoint.value === undefined) {
528 return new MissingError(operation.path);
529 }
530 endpoint.parent[endpoint.key] = operation.value;
531 return null;
532 }
533 /**
534 > The "move" operation removes the value at a specified location and
535 > adds it to the target location.
536 > The operation object MUST contain a "from" member, which is a string
537 > containing a JSON Pointer value that references the location in the
538 > target document to move the value from.
539 > This operation is functionally identical to a "remove" operation on
540 > the "from" location, followed immediately by an "add" operation at
541 > the target location with the value that was just removed.
542
543 > The "from" location MUST NOT be a proper prefix of the "path"
544 > location; i.e., a location cannot be moved into one of its children.
545
546 TODO: throw if the check described in the previous paragraph fails.
547 */
548 function move(object, operation) {
549 const from_endpoint = Pointer.fromJSON(operation.from).evaluate(object);
550 if (from_endpoint.value === undefined) {
551 return new MissingError(operation.from);
552 }
553 const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
554 if (endpoint.parent === undefined) {
555 return new MissingError(operation.path);
556 }
557 _remove(from_endpoint.parent, from_endpoint.key);
558 _add(endpoint.parent, endpoint.key, from_endpoint.value);
559 return null;
560 }
561 /**
562 > The "copy" operation copies the value at a specified location to the
563 > target location.
564 > The operation object MUST contain a "from" member, which is a string
565 > containing a JSON Pointer value that references the location in the
566 > target document to copy the value from.
567 > The "from" location MUST exist for the operation to be successful.
568
569 > This operation is functionally identical to an "add" operation at the
570 > target location using the value specified in the "from" member.
571
572 Alternatively, it's like 'move' without the 'remove'.
573 */
574 function copy(object, operation) {
575 const from_endpoint = Pointer.fromJSON(operation.from).evaluate(object);
576 if (from_endpoint.value === undefined) {
577 return new MissingError(operation.from);
578 }
579 const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
580 if (endpoint.parent === undefined) {
581 return new MissingError(operation.path);
582 }
583 _add(endpoint.parent, endpoint.key, clone(from_endpoint.value));
584 return null;
585 }
586 /**
587 > The "test" operation tests that a value at the target location is
588 > equal to a specified value.
589 > The operation object MUST contain a "value" member that conveys the
590 > value to be compared to the target location's value.
591 > The target location MUST be equal to the "value" value for the
592 > operation to be considered successful.
593 */
594 function test(object, operation) {
595 const endpoint = Pointer.fromJSON(operation.path).evaluate(object);
596 // TODO: this diffAny(...).length usage could/should be lazy
597 if (diffAny(endpoint.value, operation.value, new Pointer()).length) {
598 return new TestError(endpoint.value, operation.value);
599 }
600 return null;
601 }
602 class InvalidOperationError extends Error {
603 constructor(operation) {
604 super(`Invalid operation: ${operation.op}`);
605 this.operation = operation;
606 this.name = 'InvalidOperationError';
607 }
608 }
609 /**
610 Switch on `operation.op`, applying the corresponding patch function for each
611 case to `object`.
612 */
613 function apply(object, operation) {
614 // not sure why TypeScript can't infer typesafety of:
615 // {add, remove, replace, move, copy, test}[operation.op](object, operation)
616 // (seems like a bug)
617 switch (operation.op) {
618 case 'add': return add(object, operation);
619 case 'remove': return remove(object, operation);
620 case 'replace': return replace(object, operation);
621 case 'move': return move(object, operation);
622 case 'copy': return copy(object, operation);
623 case 'test': return test(object, operation);
624 }
625 return new InvalidOperationError(operation);
626 }
627
628 /**
629 Apply a 'application/json-patch+json'-type patch to an object.
630
631 `patch` *must* be an array of operations.
632
633 > Operation objects MUST have exactly one "op" member, whose value
634 > indicates the operation to perform. Its value MUST be one of "add",
635 > "remove", "replace", "move", "copy", or "test"; other values are
636 > errors.
637
638 This method mutates the target object in-place.
639
640 @returns list of results, one for each operation: `null` indicated success,
641 otherwise, the result will be an instance of one of the Error classes:
642 MissingError, InvalidOperationError, or TestError.
643 */
644 function applyPatch(object, patch) {
645 return patch.map(operation => apply(object, operation));
646 }
647 function wrapVoidableDiff(diff) {
648 function wrappedDiff(input, output, ptr) {
649 const custom_patch = diff(input, output, ptr);
650 // ensure an array is always returned
651 return Array.isArray(custom_patch) ? custom_patch : diffAny(input, output, ptr, wrappedDiff);
652 }
653 return wrappedDiff;
654 }
655 /**
656 Produce a 'application/json-patch+json'-type patch to get from one object to
657 another.
658
659 This does not alter `input` or `output` unless they have a property getter with
660 side-effects (which is not a good idea anyway).
661
662 `diff` is called on each pair of comparable non-primitive nodes in the
663 `input`/`output` object trees, producing nested patches. Return `undefined`
664 to fall back to default behaviour.
665
666 Returns list of operations to perform on `input` to produce `output`.
667 */
668 function createPatch(input, output, diff) {
669 const ptr = new Pointer();
670 // a new Pointer gets a default path of [''] if not specified
671 return (diff ? wrapVoidableDiff(diff) : diffAny)(input, output, ptr);
672 }
673 /**
674 Create a test operation based on `input`'s current evaluation of the JSON
675 Pointer `path`; if such a pointer cannot be resolved, returns undefined.
676 */
677 function createTest(input, path) {
678 const endpoint = Pointer.fromJSON(path).evaluate(input);
679 if (endpoint !== undefined) {
680 return { op: 'test', path, value: endpoint.value };
681 }
682 }
683 /**
684 Produce an 'application/json-patch+json'-type list of tests, to verify that
685 existing values in an object are identical to the those captured at some
686 checkpoint (whenever this function is called).
687
688 This does not alter `input` or `output` unless they have a property getter with
689 side-effects (which is not a good idea anyway).
690
691 Returns list of test operations.
692 */
693 function createTests(input, patch) {
694 const tests = new Array();
695 patch.filter(isDestructive).forEach(operation => {
696 const pathTest = createTest(input, operation.path);
697 if (pathTest)
698 tests.push(pathTest);
699 if ('from' in operation) {
700 const fromTest = createTest(input, operation.from);
701 if (fromTest)
702 tests.push(fromTest);
703 }
704 });
705 return tests;
706 }
707
708 exports.Pointer = Pointer;
709 exports.applyPatch = applyPatch;
710 exports.createPatch = createPatch;
711 exports.createTests = createTests;
712
713 Object.defineProperty(exports, '__esModule', { value: true });
714
715}));