UNPKG

@jescrich/urn

Version:
129 lines 4.42 kB
"use strict"; 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