Skip to content

Commit b78a9d2

Browse files
committed
Add browser utility functions
1 parent f33a088 commit b78a9d2

File tree

6 files changed

+143
-7
lines changed

6 files changed

+143
-7
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ value(name); // => Luke Skywalker
7777

7878
Get the JSON compatible value the document represents. Any references will
7979
have been followed so you'll never receive a `Reference` type.
80+
* typeof(browser: Browser) => JRefType
81+
82+
Works the same as the `typeof` keyword. It will return one of the JSON types
83+
(null, boolean, number, string, array, object) or "reference". If the value
84+
is not one of these types, it will throw an error.
85+
* has(key: string, browser: Browser) => boolean
86+
87+
Returns whether or not a property is present in the object that the browser
88+
represents.
89+
* length(browser: Browser) => number
90+
91+
Get the length of the array that the browser represents.
8092
* step(key: string | number, browser: Browser) => Promise\<Browser>
8193

8294
Move the browser cursor by the given "key" value. This is analogous to

lib/browser/browser.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ const followReferences = (browser) => jrefTypeOf(value(browser)) === "reference"
4747

4848
export const value = (browser) => browser._value;
4949

50+
export const typeOf = (browser) => jrefTypeOf(browser._value);
51+
export const has = (key, browser) => key in browser._value;
52+
export const length = (browser) => browser._value.length;
53+
5054
export const step = curry((key, browser) => {
5155
return followReferences({
5256
...browser,

lib/browser/utility-functions.spec.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
2+
import { MockAgent, setGlobalDispatcher } from "undici";
3+
import { get, has, length, typeOf } from "../index.js";
4+
5+
import type { Browser } from "../index.js";
6+
7+
8+
describe("JSON Browser", () => {
9+
const testDomain = "https://example.com";
10+
let mockAgent: MockAgent;
11+
12+
beforeEach(() => {
13+
mockAgent = new MockAgent();
14+
mockAgent.disableNetConnect();
15+
setGlobalDispatcher(mockAgent);
16+
});
17+
18+
afterEach(async () => {
19+
await mockAgent.close();
20+
});
21+
22+
describe("object has property", () => {
23+
let browser: Browser;
24+
25+
beforeEach(async () => {
26+
const jref = `{
27+
"foo": 42
28+
}`;
29+
30+
mockAgent.get(testDomain)
31+
.intercept({ method: "GET", path: "/foo" })
32+
.reply(200, jref, { headers: { "content-type": "application/reference+json" } });
33+
34+
browser = await get(`${testDomain}/foo`);
35+
});
36+
37+
test("true", () => {
38+
expect(has("foo", browser)).to.eql(true);
39+
});
40+
41+
test("false", () => {
42+
expect(has("bar", browser)).to.eql(false);
43+
});
44+
});
45+
46+
test("length of array", async () => {
47+
const jref = `[42]`;
48+
49+
mockAgent.get(testDomain)
50+
.intercept({ method: "GET", path: "/foo" })
51+
.reply(200, jref, { headers: { "content-type": "application/reference+json" } });
52+
53+
const uri = `${testDomain}/foo`;
54+
const browser = await get(uri);
55+
56+
expect(length(browser)).to.eql(1);
57+
});
58+
59+
describe("object has property", () => {
60+
beforeEach(() => {
61+
const jref = `{
62+
"null": null,
63+
"true": true,
64+
"false": false,
65+
"number": 42,
66+
"string": "foo",
67+
"array": [],
68+
"object": {}
69+
}`;
70+
71+
mockAgent.get(testDomain)
72+
.intercept({ method: "GET", path: "/foo" })
73+
.reply(200, jref, { headers: { "content-type": "application/reference+json" } });
74+
});
75+
76+
test("null", async () => {
77+
const browser = await get(`${testDomain}/foo#/null`);
78+
expect(typeOf(browser)).to.eql("null");
79+
});
80+
81+
test("true", async () => {
82+
const browser = await get(`${testDomain}/foo#/true`);
83+
expect(typeOf(browser)).to.eql("boolean");
84+
});
85+
86+
test("false", async () => {
87+
const browser = await get(`${testDomain}/foo#/false`);
88+
expect(typeOf(browser)).to.eql("boolean");
89+
});
90+
91+
test("number", async () => {
92+
const browser = await get(`${testDomain}/foo#/number`);
93+
expect(typeOf(browser)).to.eql("number");
94+
});
95+
96+
test("string", async () => {
97+
const browser = await get(`${testDomain}/foo#/string`);
98+
expect(typeOf(browser)).to.eql("string");
99+
});
100+
101+
test("array", async () => {
102+
const browser = await get(`${testDomain}/foo#/array`);
103+
expect(typeOf(browser)).to.eql("array");
104+
});
105+
106+
test("object", async () => {
107+
const browser = await get(`${testDomain}/foo#/object`);
108+
expect(typeOf(browser)).to.eql("object");
109+
});
110+
});
111+
});

lib/index.browser.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ addUriSchemePlugin("https", httpSchemePlugin);
1212
export {
1313
get,
1414
value,
15+
typeOf,
16+
has,
17+
length,
1518
step,
1619
iter,
1720
keys,

lib/index.d.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Response } from "undici";
2-
import type { JRef } from "./jref/index.js";
2+
import type { JRef, JRefType } from "./jref/index.js";
33

44

55
// Browser
@@ -17,12 +17,15 @@ export type Document = {
1717
};
1818

1919
export const get: <T extends Document>(uri: string, browser?: Browser) => Promise<Browser<T>>;
20-
export const value: (browser: Browser) => unknown;
21-
export const step: <T extends Document>(key: string, browser: Browser) => Promise<Browser<T>>;
22-
export const iter: <T extends Document>(browser: Browser) => AsyncGenerator<Browser<T>>;
20+
export const value: <T>(browser: Browser) => T;
21+
export const typeOf: (browser: Browser) => JRefType;
22+
export const has: (key: string, browser: Browser) => boolean;
23+
export const length: (browser: Browser) => number;
24+
export const step: (key: string, browser: Browser) => Promise<Browser>;
25+
export const iter: (browser: Browser) => AsyncGenerator<Browser>;
2326
export const keys: (browser: Browser) => Generator<string>;
24-
export const values: (browser: Browser) => AsyncGenerator<string>;
25-
export const entries: <T extends Document>(browser: Browser) => AsyncGenerator<[string, Browser<T>]>;
27+
export const values: (browser: Browser) => AsyncGenerator<Browser>;
28+
export const entries: (browser: Browser) => AsyncGenerator<[string, Browser]>;
2629

2730
export class RetrievalError extends Error {
2831
public constructor(message: string, cause: Error);
@@ -36,7 +39,7 @@ export type MediaTypePlugin<T extends Document = Document> = {
3639
quality?: number;
3740
};
3841

39-
export const addMediaTypePlugin: <T extends Document>(contentType: string, plugin: MediaTypePlugin<T>) => void;
42+
export const addMediaTypePlugin: (contentType: string, plugin: MediaTypePlugin) => void;
4043
export const removeMediaTypePlugin: (contentType: string) => void;
4144
export const setMediaTypeQuality: (contentType: string, quality: number) => void;
4245

lib/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ addUriSchemePlugin("file", fileSchemePlugin);
1414
export {
1515
get,
1616
value,
17+
typeOf,
18+
has,
19+
length,
1720
step,
1821
iter,
1922
keys,

0 commit comments

Comments
 (0)