Skip to content

Commit dd85381

Browse files
committed
feat: handle hash fragment of json pointers
1 parent b0adca5 commit dd85381

File tree

2 files changed

+70
-3
lines changed

2 files changed

+70
-3
lines changed

src/index.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,37 @@ describe("referenceResolver", () => {
8787
}
8888
});
8989
});
90+
91+
92+
describe("refs with hash fragment / internal reference component", () => {
93+
it("works with file paths", async () => {
94+
expect(await referenceResolver("./src/test-obj.json#/type", {})).toBe("string");
95+
});
96+
97+
describe("urls", () => {
98+
it("works with forward slashes surrounding the hash", async () => {
99+
expect(await referenceResolver("https://meta.open-rpc.org/#/type", {})).toBe("object");
100+
});
101+
it("works without slash infront of hash, but with one after", async () => {
102+
expect(await referenceResolver("https://meta.open-rpc.org#/type", {})).toBe("object");
103+
});
104+
105+
it("errors when the json pointer is invalid", async () => {
106+
expect.assertions(1);
107+
try {
108+
await referenceResolver("https://meta.open-rpc.org/#type", {});
109+
} catch (e) {
110+
expect(e).toBeInstanceOf(InvalidRemoteURLError);
111+
}
112+
});
113+
114+
it("errors when you have 2 hash fragments in 1 ref", async () => {
115+
expect.assertions(1);
116+
try {
117+
await referenceResolver("https://meta.open-rpc.org/#properties/#openrpc", {});
118+
} catch (e) {
119+
expect(e).toBeInstanceOf(InvalidRemoteURLError);
120+
}
121+
});
122+
});
123+
});

src/reference-resolver.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,22 +136,55 @@ export default (fetch: any, fs: any) => {
136136
}
137137
}
138138

139-
if (await fileExistsAndReadable(ref) === true) {
140-
const fileContents = await readFile(ref);
139+
// copy hash fragment from the filepath / url
140+
const hashFragmentSplit = ref.split("#");
141+
let hashFragment;
142+
if (hashFragmentSplit.length > 1) {
143+
hashFragment = hashFragmentSplit[hashFragmentSplit.length - 1];
144+
}
145+
146+
let hashlessRef = ref;
147+
if (hashFragment) {
148+
hashlessRef = ref.replace(`#${hashFragment}`, "");
149+
}
150+
151+
if (await fileExistsAndReadable(hashlessRef) === true) {
152+
// pull off the hash fragment first
153+
const fileContents = await readFile(hashlessRef);
141154
let reffedSchema;
142155
try {
143156
reffedSchema = JSON.parse(fileContents);
144157
} catch (e) {
145158
throw new NonJsonRefError({ $ref: ref }, fileContents);
146159
}
147160

161+
if (hashFragment) {
162+
try {
163+
const pointer = Ptr.parse(hashFragment);
164+
return Promise.resolve(pointer.eval(reffedSchema));
165+
} catch (e) {
166+
throw new InvalidJsonPointerRefError({ $ref: ref });
167+
}
168+
}
169+
148170
return reffedSchema;
149171
} else if (isUrlLike(ref) === false) {
150172
throw new InvalidFileSystemPathError(ref);
151173
}
152174

153175
try {
154-
return await fetch(ref).then((r: any) => r.json());
176+
// leave the hash fragment on
177+
// but evaluate the result after
178+
const result = await fetch(ref).then((r: any) => r.json());
179+
if (hashFragment) {
180+
try {
181+
const pointer = Ptr.parse(hashFragment);
182+
return Promise.resolve(pointer.eval(result));
183+
} catch (e) {
184+
throw new InvalidJsonPointerRefError({ $ref: ref });
185+
}
186+
}
187+
return result;
155188
} catch (e) {
156189
throw new InvalidRemoteURLError(ref);
157190
}

0 commit comments

Comments
 (0)