UNPKG

9.3 kBJavaScriptView Raw
1"use strict";
2var __extends = (this && this.__extends) || (function () {
3 var extendStatics = function (d, b) {
4 extendStatics = Object.setPrototypeOf ||
5 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6 function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7 return extendStatics(d, b);
8 };
9 return function (d, b) {
10 if (typeof b !== "function" && b !== null)
11 throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12 extendStatics(d, b);
13 function __() { this.constructor = d; }
14 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15 };
16})();
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.apply = exports.InvalidOperationError = exports.test = exports.copy = exports.move = exports.replace = exports.remove = exports.add = exports.TestError = exports.MissingError = void 0;
19var pointer_1 = require("./pointer");
20var util_1 = require("./util");
21var diff_1 = require("./diff");
22var MissingError = /** @class */ (function (_super) {
23 __extends(MissingError, _super);
24 function MissingError(path) {
25 var _this = _super.call(this, "Value required at path: " + path) || this;
26 _this.path = path;
27 _this.name = 'MissingError';
28 return _this;
29 }
30 return MissingError;
31}(Error));
32exports.MissingError = MissingError;
33var TestError = /** @class */ (function (_super) {
34 __extends(TestError, _super);
35 function TestError(actual, expected) {
36 var _this = _super.call(this, "Test failed: " + actual + " != " + expected) || this;
37 _this.actual = actual;
38 _this.expected = expected;
39 _this.name = 'TestError';
40 return _this;
41 }
42 return TestError;
43}(Error));
44exports.TestError = TestError;
45function _add(object, key, value) {
46 if (Array.isArray(object)) {
47 // `key` must be an index
48 if (key == '-') {
49 object.push(value);
50 }
51 else {
52 var index = parseInt(key, 10);
53 object.splice(index, 0, value);
54 }
55 }
56 else {
57 object[key] = value;
58 }
59}
60function _remove(object, key) {
61 if (Array.isArray(object)) {
62 // '-' syntax doesn't make sense when removing
63 var index = parseInt(key, 10);
64 object.splice(index, 1);
65 }
66 else {
67 // not sure what the proper behavior is when path = ''
68 delete object[key];
69 }
70}
71/**
72> o If the target location specifies an array index, a new value is
73> inserted into the array at the specified index.
74> o If the target location specifies an object member that does not
75> already exist, a new member is added to the object.
76> o If the target location specifies an object member that does exist,
77> that member's value is replaced.
78*/
79function add(object, operation) {
80 var endpoint = pointer_1.Pointer.fromJSON(operation.path).evaluate(object);
81 // it's not exactly a "MissingError" in the same way that `remove` is -- more like a MissingParent, or something
82 if (endpoint.parent === undefined) {
83 return new MissingError(operation.path);
84 }
85 _add(endpoint.parent, endpoint.key, util_1.clone(operation.value));
86 return null;
87}
88exports.add = add;
89/**
90> The "remove" operation removes the value at the target location.
91> The target location MUST exist for the operation to be successful.
92*/
93function remove(object, operation) {
94 // endpoint has parent, key, and value properties
95 var endpoint = pointer_1.Pointer.fromJSON(operation.path).evaluate(object);
96 if (endpoint.value === undefined) {
97 return new MissingError(operation.path);
98 }
99 // not sure what the proper behavior is when path = ''
100 _remove(endpoint.parent, endpoint.key);
101 return null;
102}
103exports.remove = remove;
104/**
105> The "replace" operation replaces the value at the target location
106> with a new value. The operation object MUST contain a "value" member
107> whose content specifies the replacement value.
108> The target location MUST exist for the operation to be successful.
109
110> This operation is functionally identical to a "remove" operation for
111> a value, followed immediately by an "add" operation at the same
112> location with the replacement value.
113
114Even more simply, it's like the add operation with an existence check.
115*/
116function replace(object, operation) {
117 var endpoint = pointer_1.Pointer.fromJSON(operation.path).evaluate(object);
118 if (endpoint.parent === null) {
119 return new MissingError(operation.path);
120 }
121 // this existence check treats arrays as a special case
122 if (Array.isArray(endpoint.parent)) {
123 if (parseInt(endpoint.key, 10) >= endpoint.parent.length) {
124 return new MissingError(operation.path);
125 }
126 }
127 else if (endpoint.value === undefined) {
128 return new MissingError(operation.path);
129 }
130 endpoint.parent[endpoint.key] = operation.value;
131 return null;
132}
133exports.replace = replace;
134/**
135> The "move" operation removes the value at a specified location and
136> adds it to the target location.
137> The operation object MUST contain a "from" member, which is a string
138> containing a JSON Pointer value that references the location in the
139> target document to move the value from.
140> This operation is functionally identical to a "remove" operation on
141> the "from" location, followed immediately by an "add" operation at
142> the target location with the value that was just removed.
143
144> The "from" location MUST NOT be a proper prefix of the "path"
145> location; i.e., a location cannot be moved into one of its children.
146
147TODO: throw if the check described in the previous paragraph fails.
148*/
149function move(object, operation) {
150 var from_endpoint = pointer_1.Pointer.fromJSON(operation.from).evaluate(object);
151 if (from_endpoint.value === undefined) {
152 return new MissingError(operation.from);
153 }
154 var endpoint = pointer_1.Pointer.fromJSON(operation.path).evaluate(object);
155 if (endpoint.parent === undefined) {
156 return new MissingError(operation.path);
157 }
158 _remove(from_endpoint.parent, from_endpoint.key);
159 _add(endpoint.parent, endpoint.key, from_endpoint.value);
160 return null;
161}
162exports.move = move;
163/**
164> The "copy" operation copies the value at a specified location to the
165> target location.
166> The operation object MUST contain a "from" member, which is a string
167> containing a JSON Pointer value that references the location in the
168> target document to copy the value from.
169> The "from" location MUST exist for the operation to be successful.
170
171> This operation is functionally identical to an "add" operation at the
172> target location using the value specified in the "from" member.
173
174Alternatively, it's like 'move' without the 'remove'.
175*/
176function copy(object, operation) {
177 var from_endpoint = pointer_1.Pointer.fromJSON(operation.from).evaluate(object);
178 if (from_endpoint.value === undefined) {
179 return new MissingError(operation.from);
180 }
181 var endpoint = pointer_1.Pointer.fromJSON(operation.path).evaluate(object);
182 if (endpoint.parent === undefined) {
183 return new MissingError(operation.path);
184 }
185 _add(endpoint.parent, endpoint.key, util_1.clone(from_endpoint.value));
186 return null;
187}
188exports.copy = copy;
189/**
190> The "test" operation tests that a value at the target location is
191> equal to a specified value.
192> The operation object MUST contain a "value" member that conveys the
193> value to be compared to the target location's value.
194> The target location MUST be equal to the "value" value for the
195> operation to be considered successful.
196*/
197function test(object, operation) {
198 var endpoint = pointer_1.Pointer.fromJSON(operation.path).evaluate(object);
199 // TODO: this diffAny(...).length usage could/should be lazy
200 if (diff_1.diffAny(endpoint.value, operation.value, new pointer_1.Pointer()).length) {
201 return new TestError(endpoint.value, operation.value);
202 }
203 return null;
204}
205exports.test = test;
206var InvalidOperationError = /** @class */ (function (_super) {
207 __extends(InvalidOperationError, _super);
208 function InvalidOperationError(operation) {
209 var _this = _super.call(this, "Invalid operation: " + operation.op) || this;
210 _this.operation = operation;
211 _this.name = 'InvalidOperationError';
212 return _this;
213 }
214 return InvalidOperationError;
215}(Error));
216exports.InvalidOperationError = InvalidOperationError;
217/**
218Switch on `operation.op`, applying the corresponding patch function for each
219case to `object`.
220*/
221function apply(object, operation) {
222 // not sure why TypeScript can't infer typesafety of:
223 // {add, remove, replace, move, copy, test}[operation.op](object, operation)
224 // (seems like a bug)
225 switch (operation.op) {
226 case 'add': return add(object, operation);
227 case 'remove': return remove(object, operation);
228 case 'replace': return replace(object, operation);
229 case 'move': return move(object, operation);
230 case 'copy': return copy(object, operation);
231 case 'test': return test(object, operation);
232 }
233 return new InvalidOperationError(operation);
234}
235exports.apply = apply;