UNPKG

jsonref

Version:

Javascript References ($ref) and Pointers library

129 lines 4.32 kB
import { escape } from './utils.js'; const __meta = Symbol(); export const LII_RE = /^[a-zA-Z][a-zA-Z0-9\.\-_:]*$/; // Location-independent identifier, JSON Schema draft 7, par. 8.2.3 export function normalizeUri(input, scope) { const uri = new URL(input, scope); const out = uri.toString(); return out + (!uri.hash && out[out.length - 1] !== '#' ? '#' : ''); } export function isRef(obj) { return obj !== null && typeof obj === 'object' && typeof obj.$ref === 'string'; } export function isAnnotated(obj) { return obj !== null && typeof obj === 'object' && typeof obj[__meta] === 'object'; } export function isDerefd(obj) { return isAnnotated(obj) && obj[__meta].derefd === true; } export function getMeta(obj) { if (!isAnnotated(obj)) { throw new Error('Not annotated'); } return obj[__meta]; } export function getKey(obj) { const parent = getMeta(obj).parent; if (typeof parent === 'undefined') { return undefined; } else if (Array.isArray(parent)) { for (let i = 0; i < parent.length; i++) { if (parent[i] === obj) { return i; } } return undefined; } else { return Object.keys(parent).find((k) => parent[k] === obj); } } export function getById(obj, id) { if (obj === null || typeof obj !== 'object') { throw new TypeError('Invalid object'); } const meta = getMeta(obj); return meta.registry[normalizeUri(id, meta.scope)]; } export function annotate(obj, options) { if (obj === null || typeof obj !== 'object') { throw new TypeError('Invalid object'); } else if (isAnnotated(obj)) { throw new Error('Already annotated'); } obj[__meta] = { registry: options.registry || {}, refs: options.refs || new Set(), root: obj, }; obj[__meta].registry[normalizeUri(options.scope)] = obj; return (function _annotate(obj, scope) { if (isRef(obj)) { const uri = new URL(obj.$ref, scope); uri.hash = ''; getMeta(obj).refs.add(uri.toString() + '#'); obj[__meta].scope = normalizeUri(scope); } else { if (typeof obj.$id === 'string') { if (!obj.$id || obj.$id === '#') { throw new SyntaxError(`Invalid identifier ${obj.$id}`); } const id = new URL(obj.$id, scope); if (id.hash && !id.hash.substr(1).match(LII_RE)) { throw new SyntaxError(`Invalid identifier ${obj.$id}`); } obj[__meta].scope = normalizeUri(obj.$id, scope); obj[__meta].registry[obj[__meta].scope] = obj; obj[__meta].root = obj; } else { obj[__meta].scope = normalizeUri(scope); } const keys = Object.keys(obj); for (let key of keys) { const next = obj[key]; if (next !== null && typeof next === 'object' && !isAnnotated(next)) { const meta = getMeta(obj); next[__meta] = { registry: meta.registry, refs: meta.refs, parent: obj, root: meta.root, }; _annotate(next, `${meta.scope}/${escape(key)}`); } } } return obj; })(obj, options.scope); } export function missingRefs(obj) { const meta = getMeta(obj); const known = new Set(Object.keys(meta.registry)); return [...meta.refs].filter((r) => !known.has(r)); } export function normalize(obj) { if (!isAnnotated(obj)) { throw new Error('Not annotated'); } const meta = getMeta(obj); if (typeof obj.$id === 'string') { obj.$id = normalizeUri(obj.$id, meta.scope); } const keys = Object.keys(obj); for (let key of keys) { const o = obj[key]; if (o !== null && typeof o === 'object') { if (isRef(o)) { o.$ref = normalizeUri(o.$ref, meta.scope); } else { normalize(o); } } } return obj; } //# sourceMappingURL=meta.js.map