@jescrich/urn
Version:
129 lines • 4.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Urn = void 0;
const uuid_1 = require("uuid");
class Urn {
static URN_PATTERN = /^urn:([^:]+):([^:]+)(?::([^:]+):([^:]+))*$/;
static createUUID(entity = 'uuid') {
const newId = (0, uuid_1.v4)();
return Urn.compose({ entity, id: newId });
}
static compose(parts) {
const { entity, id, attributes = {} } = parts;
if (!entity || !id) {
throw new Error("Cannot compose URN: 'entity' and 'id' are required");
}
const safeEntity = encodeURIComponent(entity);
const safeId = encodeURIComponent(id);
let urn = `urn:${safeEntity}:${safeId}`;
for (const [key, value] of Object.entries(attributes)) {
if (value === undefined || value === null) {
continue;
}
const safeKey = encodeURIComponent(key);
const safeValue = encodeURIComponent(value);
urn += `:${safeKey}:${safeValue}`;
}
if (urn.length > 255) {
throw new Error(`Composed URN is too long (${urn.length} chars, max 255)`);
}
return urn;
}
static entity(urn) {
if (!this.isValid(urn))
throw new Error('Invalid URN format');
return urn.split(':')[1];
}
static id(urn) {
if (!this.isValid(urn))
throw new Error('Invalid URN format');
return urn.split(':')[2];
}
static value(urn, key) {
if (!this.isValid(urn))
throw new Error('Invalid URN format');
const parts = urn.split(':');
for (let i = 3; i < parts.length - 1; i += 2) {
if (parts[i] === key) {
return parts[i + 1];
}
}
return null;
}
static isValid(urn) {
if (!urn || urn.length > 255) {
return false;
}
try {
const { entity, id, attributes } = Urn.parse(urn);
if (!/^[A-Za-z0-9][A-Za-z0-9-]{1,31}$/.test(entity)) {
return false;
}
return true;
}
catch {
return false;
}
}
static addAttribute(urn, key, value) {
const { entity, id, attributes } = Urn.parse(urn);
const safeKey = encodeURIComponent(key);
const safeValue = encodeURIComponent(value);
attributes[safeKey] = safeValue;
return Urn.compose({ entity, id, attributes });
}
static removeAttribute(urn, key) {
const { entity, id, attributes } = Urn.parse(urn);
if (!(key in attributes)) {
return urn;
}
delete attributes[key];
return Urn.compose({ entity, id, attributes });
}
static getAllAttributes(urn) {
if (!this.isValid(urn))
throw new Error('Invalid URN format');
const parts = urn.split(':');
const attributes = {};
for (let i = 3; i < parts.length - 1; i += 2) {
attributes[parts[i]] = parts[i + 1];
}
return attributes;
}
static vendor(urn) {
return this.value(urn, 'vendor');
}
static normalize(urn) {
const { entity, id, attributes } = Urn.parse(urn);
return Urn.compose({ entity: entity.toLowerCase(), id, attributes });
}
static parse(urn) {
if (!urn.toLowerCase().startsWith('urn:')) {
throw new Error("Invalid URN: Must start with the 'urn:' scheme");
}
const content = urn.substring(4);
const parts = content.split(':');
if (parts.length < 2) {
throw new Error('Invalid URN: Missing entity or ID component');
}
const [entity, id, ...rest] = parts;
if (!entity || !id) {
throw new Error('Invalid URN: Entity or ID is empty');
}
if (rest.length % 2 !== 0) {
throw new Error('Invalid URN: Attribute key without value');
}
const attributes = {};
for (let i = 0; i < rest.length; i += 2) {
const key = rest[i];
const value = rest[i + 1];
if (!key || !value) {
throw new Error(`Invalid URN: Attribute ${key} missing value`);
}
attributes[key] = value;
}
return { entity, id, attributes };
}
}
exports.Urn = Urn;
//# sourceMappingURL=urn.js.map