@weborigami/async-tree 0.0.66-beta.1 → 0.0.66-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/main.js +2 -0
- package/package.json +4 -4
- package/src/BrowserFileTree.js +28 -6
- package/src/DeepMapTree.js +2 -2
- package/src/DeepObjectTree.js +6 -9
- package/src/FileTree.js +14 -10
- package/src/MapTree.js +27 -4
- package/src/ObjectTree.js +53 -6
- package/src/OpenSiteTree.js +41 -0
- package/src/SetTree.js +0 -6
- package/src/SiteTree.js +24 -85
- package/src/Tree.d.ts +0 -1
- package/src/Tree.js +18 -38
- package/src/jsonKeys.js +4 -37
- package/src/operations/cache.js +0 -4
- package/src/operations/deepMerge.js +7 -10
- package/src/operations/merge.js +7 -10
- package/src/trailingSlash.js +54 -0
- package/src/transforms/cachedKeyFunctions.js +72 -34
- package/src/transforms/keyFunctionsForExtensions.js +24 -10
- package/src/transforms/mapFn.js +11 -17
- package/src/transforms/regExpKeys.js +17 -12
- package/src/utilities.js +34 -6
- package/test/BrowserFileTree.test.js +28 -5
- package/test/DeepMapTree.test.js +17 -0
- package/test/DeepObjectTree.test.js +17 -7
- package/test/FileTree.test.js +14 -7
- package/test/MapTree.test.js +21 -0
- package/test/ObjectTree.test.js +16 -12
- package/test/OpenSiteTree.test.js +113 -0
- package/test/SiteTree.test.js +14 -49
- package/test/Tree.test.js +19 -39
- package/test/browser/assert.js +9 -0
- package/test/browser/index.html +4 -4
- package/test/calendarTree.test.js +1 -1
- package/test/fixtures/markdown/subfolder/README.md +1 -0
- package/test/jsonKeys.test.js +0 -9
- package/test/operations/cache.test.js +1 -1
- package/test/operations/merge.test.js +20 -1
- package/test/trailingSlash.test.js +36 -0
- package/test/transforms/cachedKeyFunctions.test.js +90 -0
- package/test/transforms/keyFunctionsForExtensions.test.js +7 -3
- package/test/transforms/mapFn.test.js +29 -20
- package/test/utilities.test.js +6 -2
- package/test/transforms/cachedKeyMaps.test.js +0 -41
package/src/utilities.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Tree } from "./internal.js";
|
|
2
2
|
import * as symbols from "./symbols.js";
|
|
3
|
+
import * as trailingSlash from "./trailingSlash.js";
|
|
3
4
|
|
|
4
5
|
const textDecoder = new TextDecoder();
|
|
5
6
|
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
|
@@ -155,20 +156,35 @@ export function isUnpackable(obj) {
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
/**
|
|
158
|
-
* Given a path like "/foo/bar/baz", return an array of keys like ["foo",
|
|
159
|
-
* "baz"].
|
|
159
|
+
* Given a path like "/foo/bar/baz", return an array of keys like ["foo/",
|
|
160
|
+
* "bar/", "baz"].
|
|
160
161
|
*
|
|
161
|
-
* Leading slashes are ignored. Consecutive slashes
|
|
162
|
-
*
|
|
162
|
+
* Leading slashes are ignored. Consecutive slashes will be ignored. Trailing
|
|
163
|
+
* slashes are preserved.
|
|
163
164
|
*
|
|
164
165
|
* @param {string} pathname
|
|
165
166
|
*/
|
|
166
167
|
export function keysFromPath(pathname) {
|
|
167
|
-
|
|
168
|
+
// Split the path at each slash
|
|
169
|
+
let keys = pathname.split("/");
|
|
168
170
|
if (keys[0] === "") {
|
|
169
171
|
// The path begins with a slash; drop that part.
|
|
170
172
|
keys.shift();
|
|
171
173
|
}
|
|
174
|
+
if (keys.at(-1) === "") {
|
|
175
|
+
// The path ends with a slash; drop that part.
|
|
176
|
+
keys.pop();
|
|
177
|
+
}
|
|
178
|
+
// Drop any empty keys
|
|
179
|
+
keys = keys.filter((key) => key !== "");
|
|
180
|
+
// Add the trailing slash back to all keys but the last
|
|
181
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
182
|
+
keys[i] += "/";
|
|
183
|
+
}
|
|
184
|
+
// Add trailing slash to last key if path ended with a slash
|
|
185
|
+
if (keys.length > 0 && trailingSlash.has(pathname)) {
|
|
186
|
+
keys[keys.length - 1] += "/";
|
|
187
|
+
}
|
|
172
188
|
return keys;
|
|
173
189
|
}
|
|
174
190
|
|
|
@@ -283,7 +299,19 @@ export async function toPlainValue(input) {
|
|
|
283
299
|
}
|
|
284
300
|
|
|
285
301
|
function toBase64(object) {
|
|
286
|
-
|
|
302
|
+
if (typeof Buffer !== "undefined") {
|
|
303
|
+
// Node.js environment
|
|
304
|
+
return Buffer.from(object).toString("base64");
|
|
305
|
+
} else {
|
|
306
|
+
// Browser environment
|
|
307
|
+
let binary = "";
|
|
308
|
+
const bytes = new Uint8Array(object);
|
|
309
|
+
const len = bytes.byteLength;
|
|
310
|
+
for (let i = 0; i < len; i++) {
|
|
311
|
+
binary += String.fromCharCode(bytes[i]);
|
|
312
|
+
}
|
|
313
|
+
return btoa(binary);
|
|
314
|
+
}
|
|
287
315
|
}
|
|
288
316
|
|
|
289
317
|
/**
|
|
@@ -13,16 +13,14 @@ if (isBrowser) {
|
|
|
13
13
|
"Alice.md",
|
|
14
14
|
"Bob.md",
|
|
15
15
|
"Carol.md",
|
|
16
|
+
"subfolder/",
|
|
16
17
|
]);
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
test("can get the value for a key", async () => {
|
|
20
21
|
const fixture = await createFixture();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"Bob.md": "Hello, **Bob**.",
|
|
24
|
-
"Carol.md": "Hello, **Carol**.",
|
|
25
|
-
});
|
|
22
|
+
const buffer = await fixture.get("Alice.md");
|
|
23
|
+
assert.equal(text(buffer), "Hello, **Alice**.");
|
|
26
24
|
});
|
|
27
25
|
|
|
28
26
|
test("getting an unsupported key returns undefined", async () => {
|
|
@@ -30,6 +28,11 @@ if (isBrowser) {
|
|
|
30
28
|
assert.equal(await fixture.get("xyz"), undefined);
|
|
31
29
|
});
|
|
32
30
|
|
|
31
|
+
test("getting empty key returns undefined", async () => {
|
|
32
|
+
const fixture = await createFixture();
|
|
33
|
+
assert.equal(await fixture.get(""), undefined);
|
|
34
|
+
});
|
|
35
|
+
|
|
33
36
|
test("getting a null/undefined key throws an exception", async () => {
|
|
34
37
|
const fixture = await createFixture();
|
|
35
38
|
await assert.rejects(async () => {
|
|
@@ -40,6 +43,20 @@ if (isBrowser) {
|
|
|
40
43
|
});
|
|
41
44
|
});
|
|
42
45
|
|
|
46
|
+
test("sets parent on subtrees", async () => {
|
|
47
|
+
const fixture = await createFixture();
|
|
48
|
+
const subfolder = await fixture.get("subfolder");
|
|
49
|
+
assert.equal(subfolder.parent, fixture);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
53
|
+
const fixture = await createFixture();
|
|
54
|
+
assert(await fixture.get("Alice.md"));
|
|
55
|
+
assert(await fixture.get("Alice.md/"));
|
|
56
|
+
assert(await fixture.get("subfolder"));
|
|
57
|
+
assert(await fixture.get("subfolder/"));
|
|
58
|
+
});
|
|
59
|
+
|
|
43
60
|
test("can set a value", async () => {
|
|
44
61
|
const fixture = await createFixture();
|
|
45
62
|
|
|
@@ -59,6 +76,7 @@ if (isBrowser) {
|
|
|
59
76
|
"Alice.md": "Goodbye, **Alice**.",
|
|
60
77
|
"Carol.md": "Hello, **Carol**.",
|
|
61
78
|
"David.md": "Hello, **David**.",
|
|
79
|
+
subfolder: {},
|
|
62
80
|
});
|
|
63
81
|
});
|
|
64
82
|
|
|
@@ -81,6 +99,7 @@ if (isBrowser) {
|
|
|
81
99
|
more: {
|
|
82
100
|
"Ellen.md": "Hello, **Ellen**.",
|
|
83
101
|
},
|
|
102
|
+
subfolder: {},
|
|
84
103
|
});
|
|
85
104
|
});
|
|
86
105
|
});
|
|
@@ -118,6 +137,10 @@ async function createFixture() {
|
|
|
118
137
|
await createFile(subdirectory, "Bob.md", "Hello, **Bob**.");
|
|
119
138
|
await createFile(subdirectory, "Carol.md", "Hello, **Carol**.");
|
|
120
139
|
|
|
140
|
+
await subdirectory.getDirectoryHandle("subfolder", {
|
|
141
|
+
create: true,
|
|
142
|
+
});
|
|
143
|
+
|
|
121
144
|
return new BrowserFileTree(subdirectory);
|
|
122
145
|
}
|
|
123
146
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import DeepMapTree from "../src/DeepMapTree.js";
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
|
+
|
|
6
|
+
describe("DeepMapTree", () => {
|
|
7
|
+
test("returns a DeepMapTree for value that's a Map", async () => {
|
|
8
|
+
const tree = new DeepMapTree([
|
|
9
|
+
["a", 1],
|
|
10
|
+
["map", new Map([["b", 2]])],
|
|
11
|
+
]);
|
|
12
|
+
const map = await tree.get("map");
|
|
13
|
+
assert.equal(map instanceof DeepMapTree, true);
|
|
14
|
+
assert.deepEqual(await Tree.plain(map), { b: 2 });
|
|
15
|
+
assert.equal(map.parent, tree);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -4,13 +4,7 @@ import { DeepObjectTree, Tree } from "../src/internal.js";
|
|
|
4
4
|
|
|
5
5
|
describe("DeepObjectTree", () => {
|
|
6
6
|
test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
|
|
7
|
-
const tree =
|
|
8
|
-
a: 1,
|
|
9
|
-
object: {
|
|
10
|
-
b: 2,
|
|
11
|
-
},
|
|
12
|
-
array: [3],
|
|
13
|
-
});
|
|
7
|
+
const tree = createFixture();
|
|
14
8
|
|
|
15
9
|
const object = await tree.get("object");
|
|
16
10
|
assert.equal(object instanceof DeepObjectTree, true);
|
|
@@ -22,4 +16,20 @@ describe("DeepObjectTree", () => {
|
|
|
22
16
|
assert.deepEqual(await Tree.plain(array), [3]);
|
|
23
17
|
assert.equal(array.parent, tree);
|
|
24
18
|
});
|
|
19
|
+
|
|
20
|
+
test("adds trailing slashes to keys for subtrees including plain objects or arrays", async () => {
|
|
21
|
+
const tree = createFixture();
|
|
22
|
+
const keys = Array.from(await tree.keys());
|
|
23
|
+
assert.deepEqual(keys, ["a", "object/", "array/"]);
|
|
24
|
+
});
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
function createFixture() {
|
|
28
|
+
return new DeepObjectTree({
|
|
29
|
+
a: 1,
|
|
30
|
+
object: {
|
|
31
|
+
b: 2,
|
|
32
|
+
},
|
|
33
|
+
array: [3],
|
|
34
|
+
});
|
|
35
|
+
}
|
package/test/FileTree.test.js
CHANGED
|
@@ -18,6 +18,7 @@ describe("FileTree", async () => {
|
|
|
18
18
|
"Alice.md",
|
|
19
19
|
"Bob.md",
|
|
20
20
|
"Carol.md",
|
|
21
|
+
"subfolder/",
|
|
21
22
|
]);
|
|
22
23
|
});
|
|
23
24
|
|
|
@@ -33,6 +34,11 @@ describe("FileTree", async () => {
|
|
|
33
34
|
assert.equal(await fixture.get("xyz"), undefined);
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
test("getting empty key returns undefined", async () => {
|
|
38
|
+
const fixture = createFixture("fixtures/markdown");
|
|
39
|
+
assert.equal(await fixture.get(""), undefined);
|
|
40
|
+
});
|
|
41
|
+
|
|
36
42
|
test("getting a null/undefined key throws an exception", async () => {
|
|
37
43
|
const fixture = createFixture("fixtures/markdown");
|
|
38
44
|
await assert.rejects(async () => {
|
|
@@ -43,17 +49,18 @@ describe("FileTree", async () => {
|
|
|
43
49
|
});
|
|
44
50
|
});
|
|
45
51
|
|
|
46
|
-
test("
|
|
47
|
-
const fixture = createFixture("fixtures");
|
|
48
|
-
|
|
49
|
-
assert.
|
|
52
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
53
|
+
const fixture = createFixture("fixtures/markdown");
|
|
54
|
+
assert(await fixture.get("Alice.md"));
|
|
55
|
+
assert(await fixture.get("Alice.md/"));
|
|
56
|
+
assert(await fixture.get("subfolder"));
|
|
57
|
+
assert(await fixture.get("subfolder/"));
|
|
50
58
|
});
|
|
51
59
|
|
|
52
|
-
test("
|
|
60
|
+
test("sets parent on subtrees", async () => {
|
|
53
61
|
const fixture = createFixture("fixtures");
|
|
54
|
-
assert(await fixture.isKeyForSubtree("markdown"));
|
|
55
62
|
const markdown = await fixture.get("markdown");
|
|
56
|
-
assert(
|
|
63
|
+
assert.equal(markdown.parent, fixture);
|
|
57
64
|
});
|
|
58
65
|
|
|
59
66
|
test("can write out a file via set()", async () => {
|
package/test/MapTree.test.js
CHANGED
|
@@ -22,6 +22,27 @@ describe("MapTree", () => {
|
|
|
22
22
|
assert.equal(more[symbols.parent], fixture);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
test("adds trailing slashes to keys for subtrees", async () => {
|
|
26
|
+
const tree = new MapTree([
|
|
27
|
+
["a", 1],
|
|
28
|
+
["subtree", new MapTree([["b", 2]])],
|
|
29
|
+
]);
|
|
30
|
+
const keys = Array.from(await tree.keys());
|
|
31
|
+
assert.deepEqual(keys, ["a", "subtree/"]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
35
|
+
const subtree = new MapTree([["b", 2]]);
|
|
36
|
+
const tree = new MapTree([
|
|
37
|
+
["a", 1],
|
|
38
|
+
["subtree", subtree],
|
|
39
|
+
]);
|
|
40
|
+
assert.equal(await tree.get("a"), 1);
|
|
41
|
+
assert.equal(await tree.get("a/"), 1);
|
|
42
|
+
assert.equal(await tree.get("subtree"), subtree);
|
|
43
|
+
assert.equal(await tree.get("subtree/"), subtree);
|
|
44
|
+
});
|
|
45
|
+
|
|
25
46
|
test("getting an unsupported key returns undefined", async () => {
|
|
26
47
|
const fixture = createFixture();
|
|
27
48
|
assert.equal(await fixture.get("d"), undefined);
|
package/test/ObjectTree.test.js
CHANGED
|
@@ -41,18 +41,21 @@ describe("ObjectTree", () => {
|
|
|
41
41
|
c: 3,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
// Update existing key
|
|
44
|
+
// Update existing key
|
|
45
45
|
await tree.set("a", 4);
|
|
46
46
|
|
|
47
|
-
//
|
|
48
|
-
await tree.set("d", 5);
|
|
49
|
-
|
|
50
|
-
// Delete key.
|
|
47
|
+
// Delete key
|
|
51
48
|
await tree.set("b", undefined);
|
|
52
49
|
|
|
50
|
+
// Overwrite key with trailing slash
|
|
51
|
+
await tree.set("c/", {});
|
|
52
|
+
|
|
53
|
+
// New key
|
|
54
|
+
await tree.set("d", 5);
|
|
55
|
+
|
|
53
56
|
assert.deepEqual(await Tree.entries(tree), [
|
|
54
57
|
["a", 4],
|
|
55
|
-
["c",
|
|
58
|
+
["c/", {}],
|
|
56
59
|
["d", 5],
|
|
57
60
|
]);
|
|
58
61
|
});
|
|
@@ -106,7 +109,7 @@ describe("ObjectTree", () => {
|
|
|
106
109
|
assert.equal(more.parent, fixture);
|
|
107
110
|
});
|
|
108
111
|
|
|
109
|
-
test("
|
|
112
|
+
test("adds trailing slashes to keys for subtrees", async () => {
|
|
110
113
|
const tree = new ObjectTree({
|
|
111
114
|
a1: 1,
|
|
112
115
|
a2: new ObjectTree({
|
|
@@ -118,21 +121,22 @@ describe("ObjectTree", () => {
|
|
|
118
121
|
}),
|
|
119
122
|
});
|
|
120
123
|
const keys = Array.from(await tree.keys());
|
|
121
|
-
|
|
122
|
-
keys.map(async (key) => await tree.isKeyForSubtree(key))
|
|
123
|
-
);
|
|
124
|
-
assert.deepEqual(subtrees, [false, true, false, true]);
|
|
124
|
+
assert.deepEqual(keys, ["a1", "a2/", "a3", "a4/"]);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
test("
|
|
127
|
+
test("can retrieve values with optional trailing slash", async () => {
|
|
128
128
|
const subtree = {
|
|
129
129
|
async get(key) {},
|
|
130
130
|
async keys() {},
|
|
131
131
|
};
|
|
132
132
|
const tree = new ObjectTree({
|
|
133
|
+
a: 1,
|
|
133
134
|
subtree,
|
|
134
135
|
});
|
|
136
|
+
assert.equal(await tree.get("a"), 1);
|
|
137
|
+
assert.equal(await tree.get("a/"), 1);
|
|
135
138
|
assert.equal(await tree.get("subtree"), subtree);
|
|
139
|
+
assert.equal(await tree.get("subtree/"), subtree);
|
|
136
140
|
});
|
|
137
141
|
|
|
138
142
|
test("method on an object is bound to the object", async () => {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
|
+
import OpenSiteTree from "../src/OpenSiteTree.js";
|
|
4
|
+
import { Tree } from "../src/internal.js";
|
|
5
|
+
|
|
6
|
+
const textDecoder = new TextDecoder();
|
|
7
|
+
const textEncoder = new TextEncoder();
|
|
8
|
+
|
|
9
|
+
const mockHost = "https://mock";
|
|
10
|
+
|
|
11
|
+
const mockResponses = {
|
|
12
|
+
"/.keys.json": {
|
|
13
|
+
data: JSON.stringify(["about/", "index.html"]),
|
|
14
|
+
},
|
|
15
|
+
"/about": {
|
|
16
|
+
redirected: true,
|
|
17
|
+
status: 301,
|
|
18
|
+
url: "https://mock/about/",
|
|
19
|
+
},
|
|
20
|
+
"/about/.keys.json": {
|
|
21
|
+
data: JSON.stringify(["Alice.html", "Bob.html", "Carol.html"]),
|
|
22
|
+
},
|
|
23
|
+
"/about/Alice.html": {
|
|
24
|
+
data: "Hello, Alice!",
|
|
25
|
+
},
|
|
26
|
+
"/about/Bob.html": {
|
|
27
|
+
data: "Hello, Bob!",
|
|
28
|
+
},
|
|
29
|
+
"/about/Carol.html": {
|
|
30
|
+
data: "Hello, Carol!",
|
|
31
|
+
},
|
|
32
|
+
"/index.html": {
|
|
33
|
+
data: "Home page",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe("OpenSiteTree", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
mock.method(global, "fetch", mockFetch);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("can get the keys of a tree", async () => {
|
|
43
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
44
|
+
const keys = await fixture.keys();
|
|
45
|
+
assert.deepEqual(Array.from(keys), ["about/", "index.html"]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("can get a plain value for a key", async () => {
|
|
49
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
50
|
+
const arrayBuffer = await fixture.get("index.html");
|
|
51
|
+
const text = textDecoder.decode(arrayBuffer);
|
|
52
|
+
assert.equal(text, "Home page");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("getting an unsupported key returns undefined", async () => {
|
|
56
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
57
|
+
assert.equal(await fixture.get("xyz"), undefined);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("getting a null/undefined key throws an exception", async () => {
|
|
61
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
62
|
+
await assert.rejects(async () => {
|
|
63
|
+
await fixture.get(null);
|
|
64
|
+
});
|
|
65
|
+
await assert.rejects(async () => {
|
|
66
|
+
await fixture.get(undefined);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("can return a new tree for a key that redirects", async () => {
|
|
71
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
72
|
+
const about = await fixture.get("about");
|
|
73
|
+
assert(about instanceof OpenSiteTree);
|
|
74
|
+
assert.equal(about.href, "https://mock/about/");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("can convert a site to a plain object", async () => {
|
|
78
|
+
const fixture = new OpenSiteTree(mockHost);
|
|
79
|
+
// Convert buffers to strings.
|
|
80
|
+
const strings = Tree.map(fixture, (value) => textDecoder.decode(value));
|
|
81
|
+
assert.deepEqual(await Tree.plain(strings), {
|
|
82
|
+
about: {
|
|
83
|
+
"Alice.html": "Hello, Alice!",
|
|
84
|
+
"Bob.html": "Hello, Bob!",
|
|
85
|
+
"Carol.html": "Hello, Carol!",
|
|
86
|
+
},
|
|
87
|
+
"index.html": "Home page",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
async function mockFetch(href) {
|
|
93
|
+
if (!href.startsWith(mockHost)) {
|
|
94
|
+
return { status: 404 };
|
|
95
|
+
}
|
|
96
|
+
const path = href.slice(mockHost.length);
|
|
97
|
+
const mockedResponse = mockResponses[path];
|
|
98
|
+
if (mockedResponse) {
|
|
99
|
+
return Object.assign(
|
|
100
|
+
{
|
|
101
|
+
arrayBuffer: () => textEncoder.encode(mockedResponse.data).buffer,
|
|
102
|
+
ok: true,
|
|
103
|
+
status: 200,
|
|
104
|
+
text: () => mockedResponse.data,
|
|
105
|
+
},
|
|
106
|
+
mockedResponse
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
status: 404,
|
|
112
|
+
};
|
|
113
|
+
}
|
package/test/SiteTree.test.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { beforeEach, describe, mock, test } from "node:test";
|
|
3
3
|
import SiteTree from "../src/SiteTree.js";
|
|
4
|
-
import { Tree } from "../src/internal.js";
|
|
5
4
|
|
|
6
5
|
const textDecoder = new TextDecoder();
|
|
7
6
|
const textEncoder = new TextEncoder();
|
|
@@ -9,17 +8,11 @@ const textEncoder = new TextEncoder();
|
|
|
9
8
|
const mockHost = "https://mock";
|
|
10
9
|
|
|
11
10
|
const mockResponses = {
|
|
12
|
-
"/.keys.json": {
|
|
13
|
-
data: JSON.stringify(["about/"]),
|
|
14
|
-
},
|
|
15
11
|
"/about": {
|
|
16
12
|
redirected: true,
|
|
17
13
|
status: 301,
|
|
18
14
|
url: "https://mock/about/",
|
|
19
15
|
},
|
|
20
|
-
"/about/.keys.json": {
|
|
21
|
-
data: JSON.stringify(["Alice.html", "Bob.html", "Carol.html"]),
|
|
22
|
-
},
|
|
23
16
|
"/about/Alice.html": {
|
|
24
17
|
data: "Hello, Alice!",
|
|
25
18
|
},
|
|
@@ -29,6 +22,9 @@ const mockResponses = {
|
|
|
29
22
|
"/about/Carol.html": {
|
|
30
23
|
data: "Hello, Carol!",
|
|
31
24
|
},
|
|
25
|
+
"/index.html": {
|
|
26
|
+
data: "Home page",
|
|
27
|
+
},
|
|
32
28
|
};
|
|
33
29
|
|
|
34
30
|
describe("SiteTree", () => {
|
|
@@ -36,29 +32,24 @@ describe("SiteTree", () => {
|
|
|
36
32
|
mock.method(global, "fetch", mockFetch);
|
|
37
33
|
});
|
|
38
34
|
|
|
39
|
-
test("
|
|
35
|
+
test("returns an empty array as the keys of a tree", async () => {
|
|
40
36
|
const fixture = new SiteTree(mockHost);
|
|
41
|
-
const
|
|
42
|
-
assert.
|
|
37
|
+
const keys = await fixture.keys();
|
|
38
|
+
assert.deepEqual(Array.from(keys), []);
|
|
43
39
|
});
|
|
44
40
|
|
|
45
|
-
test("can get
|
|
41
|
+
test("can get a plain value for a key", async () => {
|
|
46
42
|
const fixture = new SiteTree(mockHost);
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
assert.
|
|
50
|
-
"Alice.html",
|
|
51
|
-
"Bob.html",
|
|
52
|
-
"Carol.html",
|
|
53
|
-
]);
|
|
43
|
+
const arrayBuffer = await fixture.get("index.html");
|
|
44
|
+
const text = textDecoder.decode(arrayBuffer);
|
|
45
|
+
assert.equal(text, "Home page");
|
|
54
46
|
});
|
|
55
47
|
|
|
56
|
-
test("
|
|
48
|
+
test("immediately return a new tree for a key with a trailing slash", async () => {
|
|
57
49
|
const fixture = new SiteTree(mockHost);
|
|
58
|
-
const about = fixture.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
assert.equal(text, "Hello, Alice!");
|
|
50
|
+
const about = await fixture.get("about/");
|
|
51
|
+
assert(about instanceof SiteTree);
|
|
52
|
+
assert.equal(about.href, "https://mock/about/");
|
|
62
53
|
});
|
|
63
54
|
|
|
64
55
|
test("getting an unsupported key returns undefined", async () => {
|
|
@@ -75,32 +66,6 @@ describe("SiteTree", () => {
|
|
|
75
66
|
await fixture.get(undefined);
|
|
76
67
|
});
|
|
77
68
|
});
|
|
78
|
-
|
|
79
|
-
test("a redirect on a site with keys returns a SiteTree for the new URL", async () => {
|
|
80
|
-
const fixture = new SiteTree(mockHost);
|
|
81
|
-
const about = await fixture.get("about");
|
|
82
|
-
assert.equal(about.href, "https://mock/about/");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("can determine whether a key is for a subtree", async () => {
|
|
86
|
-
const fixture = new SiteTree(mockHost);
|
|
87
|
-
assert.equal(await fixture.isKeyForSubtree("about"), true);
|
|
88
|
-
const about = fixture.resolve("about");
|
|
89
|
-
assert.equal(await about.isKeyForSubtree("Alice.html"), false);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("can convert a SiteGraph to a plain object", async () => {
|
|
93
|
-
const fixture = new SiteTree(mockHost);
|
|
94
|
-
// Convert buffers to strings.
|
|
95
|
-
const strings = Tree.map(fixture, (value) => textDecoder.decode(value));
|
|
96
|
-
assert.deepEqual(await Tree.plain(strings), {
|
|
97
|
-
about: {
|
|
98
|
-
"Alice.html": "Hello, Alice!",
|
|
99
|
-
"Bob.html": "Hello, Bob!",
|
|
100
|
-
"Carol.html": "Hello, Carol!",
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
69
|
});
|
|
105
70
|
|
|
106
71
|
async function mockFetch(href) {
|
package/test/Tree.test.js
CHANGED
|
@@ -31,7 +31,8 @@ describe("Tree", () => {
|
|
|
31
31
|
const result = await Tree.assign(target, source);
|
|
32
32
|
|
|
33
33
|
assert.equal(result, target);
|
|
34
|
-
|
|
34
|
+
const plain = await Tree.plain(target);
|
|
35
|
+
assert.deepEqual(plain, {
|
|
35
36
|
a: 4,
|
|
36
37
|
c: 5,
|
|
37
38
|
more: {
|
|
@@ -171,17 +172,6 @@ describe("Tree", () => {
|
|
|
171
172
|
assert(Tree.isTreelike(new Set()));
|
|
172
173
|
});
|
|
173
174
|
|
|
174
|
-
test("isKeyForSubtree() returns true if the key is for a subtree", async () => {
|
|
175
|
-
const tree = new DeepObjectTree({
|
|
176
|
-
a: 1,
|
|
177
|
-
more: {
|
|
178
|
-
b: 2,
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
assert(!(await Tree.isKeyForSubtree(tree, "a")));
|
|
182
|
-
assert(await Tree.isKeyForSubtree(tree, "more"));
|
|
183
|
-
});
|
|
184
|
-
|
|
185
175
|
test("map() maps values", async () => {
|
|
186
176
|
const tree = new DeepObjectTree({
|
|
187
177
|
a: "Alice",
|
|
@@ -228,18 +218,26 @@ describe("Tree", () => {
|
|
|
228
218
|
});
|
|
229
219
|
|
|
230
220
|
test("plain() produces a plain object version of a tree", async () => {
|
|
231
|
-
const
|
|
221
|
+
const tree = new ObjectTree({
|
|
232
222
|
a: 1,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
d: 4,
|
|
237
|
-
e: 5,
|
|
223
|
+
// Slashes should be normalized
|
|
224
|
+
"sub1/": {
|
|
225
|
+
b: 2,
|
|
238
226
|
},
|
|
239
|
-
|
|
240
|
-
|
|
227
|
+
sub2: {
|
|
228
|
+
c: 3,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
241
231
|
const plain = await Tree.plain(tree);
|
|
242
|
-
assert.deepEqual(plain,
|
|
232
|
+
assert.deepEqual(plain, {
|
|
233
|
+
a: 1,
|
|
234
|
+
sub1: {
|
|
235
|
+
b: 2,
|
|
236
|
+
},
|
|
237
|
+
sub2: {
|
|
238
|
+
c: 3,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
243
241
|
});
|
|
244
242
|
|
|
245
243
|
test("plain() produces an array for an array-like tree", async () => {
|
|
@@ -335,24 +333,6 @@ describe("Tree", () => {
|
|
|
335
333
|
assert.equal(await Tree.traverse(tree, "a", "b", "c"), "Hello");
|
|
336
334
|
});
|
|
337
335
|
|
|
338
|
-
test("traversing a final empty string can unpack the last value", async () => {
|
|
339
|
-
const unpackable = new String();
|
|
340
|
-
/** @type {any} */ (unpackable).unpack = () => "Content";
|
|
341
|
-
const tree = {
|
|
342
|
-
unpackable,
|
|
343
|
-
};
|
|
344
|
-
const result = await Tree.traverse(tree, "unpackable", "");
|
|
345
|
-
assert.equal(result, "Content");
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
test("traversing a final empty string returns a value as is if it's not unpackable", async () => {
|
|
349
|
-
const tree = new ObjectTree({
|
|
350
|
-
a: "Hello",
|
|
351
|
-
});
|
|
352
|
-
const result = await Tree.traverse(tree, "a", "");
|
|
353
|
-
assert.equal(result, "Hello");
|
|
354
|
-
});
|
|
355
|
-
|
|
356
336
|
test("traversePath() traverses a slash-separated path", async () => {
|
|
357
337
|
const tree = new ObjectTree({
|
|
358
338
|
a: {
|
package/test/browser/assert.js
CHANGED
|
@@ -43,3 +43,12 @@ assert.deepEqual = (actual, expected) => {
|
|
|
43
43
|
|
|
44
44
|
throw new Error(`Expected ${expected} but got ${actual}`);
|
|
45
45
|
};
|
|
46
|
+
|
|
47
|
+
assert.rejects = async (promise) => {
|
|
48
|
+
try {
|
|
49
|
+
await promise;
|
|
50
|
+
throw new Error("Expected promise to reject but it resolved");
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
};
|
package/test/browser/index.html
CHANGED
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
<script type="module" src="../Tree.test.js"></script>
|
|
23
23
|
<script type="module" src="../operations/cache.test.js"></script>
|
|
24
24
|
<script type="module" src="../operations/merge.test.js"></script>
|
|
25
|
-
<script type="module" src="../operations/
|
|
26
|
-
<script type="module" src="../transforms/
|
|
25
|
+
<script type="module" src="../operations/deepMerge.test.js"></script>
|
|
26
|
+
<script type="module" src="../transforms/cachedKeyMaps.test.js"></script>
|
|
27
27
|
<script
|
|
28
28
|
type="module"
|
|
29
|
-
src="../transforms/
|
|
29
|
+
src="../transforms/keyFunctionsForExtensions.test.js"
|
|
30
30
|
></script>
|
|
31
|
-
<script type="module" src="../transforms/
|
|
31
|
+
<script type="module" src="../transforms/mapFn.test.js"></script>
|
|
32
32
|
<script type="module" src="../utilities.test.js"></script>
|
|
33
33
|
</head>
|
|
34
34
|
<body></body>
|