urns
Version:
An RFC 8141 compliant URN library with some interesting type related functionality
234 lines (233 loc) • 9.42 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.URNSpace = void 0;
var parser_1 = require("./parser");
/**
* The `URNSpace` class allows you to define a space of URNs defined
* by a common namespace identifier (NID). You can further restrict
* this space by providing your own subtype of string for the
* namespace specific string (NSS) as well. Furthermore, via the
* `options` you can provide your own functions for validating
* the NSS (see `is` functionality) and potentially providing
* further semantic parsing of the NSS (see `transform` functionality).
*
* With an instance of `URNSpace` in hand, it becomes very easy to
* create new URNs and validate/parse existing URNs that belong
* to the space.
*/
var URNSpace = /** @class */ (function () {
/**
* Create a new URNSpace with the specifid NID
* @param nid Namespace identifer (NID)
* @param options
*/
function URNSpace(nid, options) {
this.nid = nid;
this.options = options;
}
// this is here so the enduser can easilu overwrite these for instance to use caching
// in derived class
URNSpace.prototype._parseURN = function (s) {
return parser_1.parseURN(s);
};
URNSpace.prototype._createURN = function (nid, nss) {
var _a;
return parser_1.createURN(nid, nss, (_a = this.options) === null || _a === void 0 ? void 0 : _a.skipVerification);
};
URNSpace.prototype._createFullURN = function (nid, nss, components) {
var _a;
return parser_1.createFullURN(nid, nss, components, (_a = this.options) === null || _a === void 0 ? void 0 : _a.skipVerification);
};
/**
* Create a new URN in this namespace. The type parameter `N` here
* can be a subtype of the `NSS` type parameter of the `URNSpace` itself.
* This allows you to deliberately create URNs that are part of a
* more narrow subspace.
*/
URNSpace.prototype.urn = function (nss) {
var _a;
var createdURN;
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.encode) {
createdURN = this._createURN(this.nid, this.options.encode(nss));
}
else {
createdURN = this._createURN(this.nid, nss);
}
try {
this.assume(createdURN);
return createdURN;
}
catch (e) {
throw e;
}
};
/**
* This creates a URN that will no longer conform to the BaseURN type (which assumes no components).
* But it is still useful to create full URNs (with components) within a space.
**/
URNSpace.prototype.fullUrn = function (nss, components) {
var _a;
var createdURN;
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.encode) {
createdURN = this._createFullURN(this.nid, this.options.encode(nss), components);
}
else {
createdURN = this._createFullURN(this.nid, nss, components);
}
return createdURN;
};
/**
* This is the main benefit of a `URNSpace`, it allows you to perform a runtime
* check that narrows the scope of an ordinary string down to that of a member
* of this URNSpace. This is useful if, for example, you are deserializing
* content (e.g., from a JSON payload) and you want to ensure that a given
* string is in fact of the (URN) type you expect.
*
* NB - This confirms that the URN is a base URN (no components). If you are
* dealing with a full URN, use isFull
* @param s
* @returns
*/
URNSpace.prototype.is = function (s) {
/**
* Assume it is in this space and then check for exceptions.
*
* Note: this might prove more expensive in practice in which case you could use an
* alternative formulation of the `assume` method here but changing how it addresses
* each contingency. I opted for code reuse over optimization here. Time will tell
* if that was the right call.
*/
try {
this.assume(s);
return true;
}
catch (e) {
return false;
}
};
/**
* Check if this string qualifies as a full URN belonging to this URN space.
*
* @param s A string which may be a full URN in this space, or it may not
* @returns
*/
URNSpace.prototype.isFull = function (s) {
var _a;
/**
* Assume it is in this space and then check for exceptions.
*
* Note: this might prove more expensive in practice in which case you could use an
* alternative formulation of the `assume` method here but changing how it addresses
* each contingency. I opted for code reuse over optimization here. Time will tell
* if that was the right call.
*/
try {
/** We start by parsing the string as a URN */
var parsed = this._parseURN(s);
/** First check the NID */
if (parsed.nid !== this.nid)
return false;
/** Next, check the NSS if a predicate is defined */
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.pred) {
if (!this.options.pred(parsed.nss)) {
return false;
}
}
return true;
}
catch (e) {
return false;
}
};
/**
* This function is like `is`, but it assumes the result will be true. This can save you some
* condition handling. You should use this when you have a high degree of confidence that the
* string does actually conform this URNSpace because it will throw an exception if it doesn't.
* @param s
* @returns
*/
URNSpace.prototype.assume = function (s) {
var _a, _b;
/** We start by parsing the string as a URN */
var parsed = this._parseURN(s);
/** Then we confirm that it conforms to the type of `BaseURN<NID, NSS>`. */
if (parsed.nid === this.nid &&
parsed.rcomponent === null &&
parsed.qcomponent === null &&
parsed.fragment === null) {
/**
* If there is an optional `pred` function provided for this space,
* run it to perform further semantic validation on the NSS.
*/
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.pred) {
if (!this.options.pred(parsed.nss)) {
throw new Error("Assumption that '" + s + "' belongs to the specified URNSpace('" + this.nid + "') is faulty, predicate failed");
}
}
/**
* Now check if there is an optional transformational process
* defined and ensure that it runs without throwing an exception.
*/
if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.decode) {
try {
this.options.decode(parsed.nss);
}
catch (e) {
throw new Error("Assumption that '" + s + "' belongs to the specified URNSpace('" + this.nid + "') fails in decoding: " + e.message);
}
}
/** If we get here, the NSS has passed all further validation we can do. */
return s;
}
throw new Error("Assumption that '" + s + "' belongs to the specified URNSpace('" + this.nid + "') is faulty");
};
/**
* This function parses the provided URN and also invokes the optional `decode` function (if provided).
* @param urn
* @returns
*/
URNSpace.prototype.parse = function (urn) {
var parsed = this._parseURN(urn);
if (!this.isFull(urn)) {
throw new Error("Assumption that '" + urn + "' belongs to the specified URNSpace('" + this.nid + "') is faulty");
}
try {
var decoded = this.options && this.options.decode
? this.options.decode(parsed.nss)
: {};
return __assign(__assign({}, parsed), { decoded: decoded });
}
catch (e) {
throw new Error("Assumption that '" + urn + "' belongs to the specified URNSpace('" + this.nid + "') fails in decoding: " + e.message);
}
};
/**
* This helper function is for the use case where you simply want to extract the NSS value
* of a provided string.
* @param urn
* @returns Namespace specific string
*/
URNSpace.prototype.nss = function (urn) {
return this.parse(urn).nss;
};
/**
* This is another helper function that provides the result of the optional `transform`
* function if provided. Otherwise, it simply returns `{}`.
*/
URNSpace.prototype.decode = function (urn) {
return this.parse(urn).decoded;
};
return URNSpace;
}());
exports.URNSpace = URNSpace;