rfc6902
Version:
Complete implementation of RFC6902 (patch and diff)
106 lines (98 loc) • 3.73 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Pointer = exports.escapeToken = exports.unescapeToken = void 0;
/**
Unescape token part of a JSON Pointer string
`token` should *not* contain any '/' characters.
> Evaluation of each reference token begins by decoding any escaped
> character sequence. This is performed by first transforming any
> occurrence of the sequence '~1' to '/', and then transforming any
> occurrence of the sequence '~0' to '~'. By performing the
> substitutions in this order, an implementation avoids the error of
> turning '~01' first into '~1' and then into '/', which would be
> incorrect (the string '~01' correctly becomes '~1' after
> transformation).
Here's my take:
~1 is unescaped with higher priority than ~0 because it is a lower-order escape character.
I say "lower order" because '/' needs escaping due to the JSON Pointer serialization technique.
Whereas, '~' is escaped because escaping '/' uses the '~' character.
*/
function unescapeToken(token) {
return token.replace(/~1/g, '/').replace(/~0/g, '~');
}
exports.unescapeToken = unescapeToken;
/** Escape token part of a JSON Pointer string
> '~' needs to be encoded as '~0' and '/'
> needs to be encoded as '~1' when these characters appear in a
> reference token.
This is the exact inverse of `unescapeToken()`, so the reverse replacements must take place in reverse order.
*/
function escapeToken(token) {
return token.replace(/~/g, '~0').replace(/\//g, '~1');
}
exports.escapeToken = escapeToken;
/**
JSON Pointer representation
*/
var Pointer = /** @class */ (function () {
function Pointer(tokens) {
if (tokens === void 0) { tokens = ['']; }
this.tokens = tokens;
}
/**
`path` *must* be a properly escaped string.
*/
Pointer.fromJSON = function (path) {
var tokens = path.split('/').map(unescapeToken);
if (tokens[0] !== '')
throw new Error("Invalid JSON Pointer: ".concat(path));
return new Pointer(tokens);
};
Pointer.prototype.toString = function () {
return this.tokens.map(escapeToken).join('/');
};
/**
Returns an object with 'parent', 'key', and 'value' properties.
In the special case that this Pointer's path == "",
this object will be {parent: null, key: '', value: object}.
Otherwise, parent and key will have the property such that parent[key] == value.
*/
Pointer.prototype.evaluate = function (object) {
var parent = null;
var key = '';
var value = object;
for (var i = 1, l = this.tokens.length; i < l; i++) {
parent = value;
key = this.tokens[i];
if (key == '__proto__' || key == 'constructor' || key == 'prototype') {
continue;
}
// not sure if this the best way to handle non-existant paths...
value = (parent || {})[key];
}
return { parent: parent, key: key, value: value };
};
Pointer.prototype.get = function (object) {
return this.evaluate(object).value;
};
Pointer.prototype.set = function (object, value) {
var endpoint = this.evaluate(object);
if (endpoint.parent) {
endpoint.parent[endpoint.key] = value;
}
};
Pointer.prototype.push = function (token) {
// mutable
this.tokens.push(token);
};
/**
`token` should be a String. It'll be coerced to one anyway.
immutable (shallowly)
*/
Pointer.prototype.add = function (token) {
var tokens = this.tokens.concat(String(token));
return new Pointer(tokens);
};
return Pointer;
}());
exports.Pointer = Pointer;
;