diff --git a/src/index.test.ts b/src/index.test.ts index 81af33c..fac48ce 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -87,3 +87,48 @@ describe("referenceResolver", () => { } }); }); + + +describe("refs with hash fragment / internal reference component", () => { + describe("files", () => { + it("works in simple case", async () => { + expect(await referenceResolver("./src/test-obj.json#/type", {})).toBe("string"); + }); + + it("errors when the json pointer is invalid", async () => { + expect.assertions(1); + try { + await referenceResolver("./src/test-obj.json#balony", {}); + } catch (e) { + expect(e).toBeInstanceOf(InvalidJsonPointerRefError); + } + }); + }); + + describe("urls", () => { + it("works with forward slashes surrounding the hash", async () => { + expect(await referenceResolver("https://meta.open-rpc.org/#/type", {})).toBe("object"); + }); + it("works without slash infront of hash, but with one after", async () => { + expect(await referenceResolver("https://meta.open-rpc.org#/type", {})).toBe("object"); + }); + + it("errors when the json pointer is invalid", async () => { + expect.assertions(1); + try { + await referenceResolver("https://meta.open-rpc.org/#type", {}); + } catch (e) { + expect(e).toBeInstanceOf(InvalidJsonPointerRefError); + } + }); + + it("errors when you have 2 hash fragments in 1 ref", async () => { + expect.assertions(1); + try { + await referenceResolver("https://meta.open-rpc.org/#properties/#openrpc", {}); + } catch (e) { + expect(e).toBeInstanceOf(InvalidJsonPointerRefError); + } + }); + }); +}); diff --git a/src/reference-resolver.ts b/src/reference-resolver.ts index b3d9f53..8d2b915 100644 --- a/src/reference-resolver.ts +++ b/src/reference-resolver.ts @@ -136,8 +136,19 @@ export default (fetch: any, fs: any) => { } } - if (await fileExistsAndReadable(ref) === true) { - const fileContents = await readFile(ref); + const hashFragmentSplit = ref.split("#"); + let hashFragment; + if (hashFragmentSplit.length > 1) { + hashFragment = hashFragmentSplit[hashFragmentSplit.length - 1]; + } + + let hashlessRef = ref; + if (hashFragment) { + hashlessRef = ref.replace(`#${hashFragment}`, ""); + } + + if (await fileExistsAndReadable(hashlessRef) === true) { + const fileContents = await readFile(hashlessRef); let reffedSchema; try { reffedSchema = JSON.parse(fileContents); @@ -145,16 +156,37 @@ export default (fetch: any, fs: any) => { throw new NonJsonRefError({ $ref: ref }, fileContents); } + if (hashFragment) { + try { + const pointer = Ptr.parse(hashFragment); + return Promise.resolve(pointer.eval(reffedSchema)); + } catch (e) { + throw new InvalidJsonPointerRefError({ $ref: ref }); + } + } + return reffedSchema; } else if (isUrlLike(ref) === false) { throw new InvalidFileSystemPathError(ref); } + let result; try { - return await fetch(ref).then((r: any) => r.json()); + result = await fetch(ref).then((r: any) => r.json()); } catch (e) { throw new InvalidRemoteURLError(ref); } + + if (hashFragment) { + try { + const pointer = Ptr.parse(hashFragment); + return Promise.resolve(pointer.eval(result)); + } catch (e) { + throw new InvalidJsonPointerRefError({ $ref: ref }); + } + } + + return result; }; return resolveReference;