koas-core
Version:
> [Koa][] + [OpenAPI Specification][] = Koas
82 lines (81 loc) • 2.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createResolver = exports.unescapeJsonPointer = exports.escapeJsonPointer = void 0;
const util_1 = require("util");
/**
* Escape a JSON pointer segment.
*
* See https://tools.ietf.org/html/rfc6901#section-3
*
* @param pointer - THe JSON pointer segment to escape.
* @returns The escaped JSON pointer segment.
*/
function escapeJsonPointer(pointer) {
return pointer.replace(/~/g, '~0').replace(/\//g, '~1');
}
exports.escapeJsonPointer = escapeJsonPointer;
/**
* Unescape a JSON pointer segment.
*
* See https://tools.ietf.org/html/rfc6901#section-3
*
* @param pointer - The JSON pointer segment to unescape
* @returns The unescaped JSON pointer segment.
*/
function unescapeJsonPointer(pointer) {
return pointer.replace(/~1/g, '/').replace(/~0/g, '~');
}
exports.unescapeJsonPointer = unescapeJsonPointer;
/**
* Create a function to resolve a JSON pointer from an OpenAPI document with caching.
*
* @param document - The OpenAPI document to create a resolver for.
* @returns A JSON ref resolver.
*/
function createResolver(document) {
const cache = new Map();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const resolver = (ref, stack) => {
// Null, undefined
if (!ref) {
return ref;
}
// Primitives
if (typeof ref !== 'object') {
return ref;
}
// Non reference objects
if (!('$ref' in ref)) {
return ref;
}
const { $ref } = ref;
if (typeof $ref !== 'string') {
throw new TypeError(`Invalid JSON pointer. Expected a string. Got: ${(0, util_1.inspect)($ref)}`);
}
if (stack.includes($ref)) {
throw new Error(`Circular JSON reference found: ${stack
.map((frame) => (0, util_1.inspect)(frame))
.concat($ref)
.join(' → ')}`);
}
if (cache.has($ref)) {
return cache.get($ref);
}
const [hashtag, ...segments] = $ref.split('/').map(unescapeJsonPointer);
if (hashtag !== '#') {
throw new TypeError(`Invalid JSON pointer. It should start with '#/'. Got: ${(0, util_1.inspect)($ref)}`);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = segments.reduce((acc, name) => {
if (acc && typeof acc === 'object' && name in acc) {
return acc[name];
}
throw new Error(`Unresolved JSON pointer: ${(0, util_1.inspect)($ref)}. Property ${(0, util_1.inspect)(name)} could not be resolved.`);
}, document);
const dereferenced = resolver(result, [...stack, $ref]);
cache.set($ref, dereferenced);
return dereferenced;
};
return (ref) => resolver(ref, []);
}
exports.createResolver = createResolver;