@weborigami/async-tree 0.5.8 → 0.6.1

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.
Files changed (182) hide show
  1. package/browser.js +1 -1
  2. package/index.ts +43 -35
  3. package/main.js +1 -2
  4. package/package.json +4 -7
  5. package/shared.js +74 -12
  6. package/src/Tree.js +11 -2
  7. package/src/drivers/AsyncMap.js +237 -0
  8. package/src/drivers/{BrowserFileTree.js → BrowserFileMap.js} +54 -38
  9. package/src/drivers/{calendarTree.js → CalendarMap.js} +80 -63
  10. package/src/drivers/ConstantMap.js +28 -0
  11. package/src/drivers/{ExplorableSiteTree.js → ExplorableSiteMap.js} +7 -7
  12. package/src/drivers/FileMap.js +238 -0
  13. package/src/drivers/{FunctionTree.js → FunctionMap.js} +19 -22
  14. package/src/drivers/ObjectMap.js +151 -0
  15. package/src/drivers/SetMap.js +13 -0
  16. package/src/drivers/{SiteTree.js → SiteMap.js} +17 -20
  17. package/src/drivers/SyncMap.js +260 -0
  18. package/src/jsonKeys.d.ts +2 -2
  19. package/src/jsonKeys.js +20 -5
  20. package/src/operations/addNextPrevious.js +35 -36
  21. package/src/operations/assign.js +27 -23
  22. package/src/operations/cache.js +30 -36
  23. package/src/operations/cachedKeyFunctions.js +1 -1
  24. package/src/operations/calendar.js +5 -0
  25. package/src/operations/child.js +35 -0
  26. package/src/operations/clear.js +13 -12
  27. package/src/operations/constant.js +5 -0
  28. package/src/operations/deepEntries.js +23 -0
  29. package/src/operations/deepMap.js +9 -9
  30. package/src/operations/deepMerge.js +36 -25
  31. package/src/operations/deepReverse.js +23 -16
  32. package/src/operations/deepTake.js +7 -7
  33. package/src/operations/deepText.js +4 -4
  34. package/src/operations/deepValues.js +3 -6
  35. package/src/operations/deepValuesIterator.js +11 -11
  36. package/src/operations/delete.js +8 -12
  37. package/src/operations/entries.js +17 -10
  38. package/src/operations/filter.js +9 -7
  39. package/src/operations/first.js +12 -10
  40. package/src/operations/forEach.js +10 -13
  41. package/src/operations/from.js +30 -39
  42. package/src/operations/globKeys.js +22 -17
  43. package/src/operations/group.js +2 -2
  44. package/src/operations/groupBy.js +24 -22
  45. package/src/operations/has.js +7 -9
  46. package/src/operations/indent.js +2 -2
  47. package/src/operations/inners.js +19 -15
  48. package/src/operations/invokeFunctions.js +22 -10
  49. package/src/operations/isAsyncMutableTree.js +5 -12
  50. package/src/operations/isAsyncTree.js +5 -20
  51. package/src/operations/isMap.js +39 -0
  52. package/src/operations/isMaplike.js +34 -0
  53. package/src/operations/isReadOnlyMap.js +14 -0
  54. package/src/operations/isTraversable.js +3 -3
  55. package/src/operations/isTreelike.js +5 -30
  56. package/src/operations/json.js +4 -12
  57. package/src/operations/keys.js +17 -8
  58. package/src/operations/length.js +9 -8
  59. package/src/operations/map.js +28 -30
  60. package/src/operations/mapExtension.js +20 -16
  61. package/src/operations/mapReduce.js +38 -24
  62. package/src/operations/mask.js +54 -29
  63. package/src/operations/match.js +13 -9
  64. package/src/operations/merge.js +43 -35
  65. package/src/operations/paginate.js +26 -18
  66. package/src/operations/parent.js +7 -7
  67. package/src/operations/paths.js +20 -22
  68. package/src/operations/plain.js +6 -6
  69. package/src/operations/reduce.js +16 -0
  70. package/src/operations/regExpKeys.js +21 -12
  71. package/src/operations/reverse.js +21 -15
  72. package/src/operations/root.js +6 -5
  73. package/src/operations/scope.js +31 -26
  74. package/src/operations/set.js +20 -0
  75. package/src/operations/shuffle.js +23 -16
  76. package/src/operations/size.js +13 -0
  77. package/src/operations/sort.js +55 -40
  78. package/src/operations/sync.js +14 -0
  79. package/src/operations/take.js +23 -11
  80. package/src/operations/text.js +4 -4
  81. package/src/operations/toFunction.js +7 -7
  82. package/src/operations/traverse.js +4 -4
  83. package/src/operations/traverseOrThrow.js +18 -9
  84. package/src/operations/traversePath.js +2 -2
  85. package/src/operations/values.js +18 -9
  86. package/src/operations/withKeys.js +22 -16
  87. package/src/symbols.js +1 -0
  88. package/src/utilities/castArraylike.js +24 -13
  89. package/src/utilities/getMapArgument.js +38 -0
  90. package/src/utilities/getParent.js +2 -2
  91. package/src/utilities/isStringlike.js +7 -5
  92. package/src/utilities/setParent.js +7 -7
  93. package/src/utilities/toFunction.js +2 -2
  94. package/src/utilities/toPlainValue.js +21 -19
  95. package/test/SampleAsyncMap.js +34 -0
  96. package/test/browser/assert.js +20 -0
  97. package/test/browser/index.html +53 -21
  98. package/test/drivers/AsyncMap.test.js +119 -0
  99. package/test/drivers/{BrowserFileTree.test.js → BrowserFileMap.test.js} +50 -33
  100. package/test/drivers/{calendarTree.test.js → CalendarMap.test.js} +17 -19
  101. package/test/drivers/ConstantMap.test.js +15 -0
  102. package/test/drivers/{ExplorableSiteTree.test.js → ExplorableSiteMap.test.js} +29 -14
  103. package/test/drivers/FileMap.test.js +156 -0
  104. package/test/drivers/FunctionMap.test.js +56 -0
  105. package/test/drivers/ObjectMap.test.js +194 -0
  106. package/test/drivers/SetMap.test.js +35 -0
  107. package/test/drivers/{SiteTree.test.js → SiteMap.test.js} +14 -10
  108. package/test/drivers/SyncMap.test.js +335 -0
  109. package/test/jsonKeys.test.js +18 -6
  110. package/test/operations/addNextPrevious.test.js +3 -2
  111. package/test/operations/assign.test.js +30 -35
  112. package/test/operations/cache.test.js +17 -12
  113. package/test/operations/cachedKeyFunctions.test.js +12 -9
  114. package/test/operations/child.test.js +34 -0
  115. package/test/operations/clear.test.js +6 -27
  116. package/test/operations/deepEntries.test.js +32 -0
  117. package/test/operations/deepMerge.test.js +23 -16
  118. package/test/operations/deepReverse.test.js +2 -2
  119. package/test/operations/deepTake.test.js +2 -2
  120. package/test/operations/deepText.test.js +4 -4
  121. package/test/operations/deepValuesIterator.test.js +2 -2
  122. package/test/operations/delete.test.js +2 -2
  123. package/test/operations/extensionKeyFunctions.test.js +6 -5
  124. package/test/operations/filter.test.js +3 -3
  125. package/test/operations/from.test.js +25 -31
  126. package/test/operations/globKeys.test.js +9 -9
  127. package/test/operations/groupBy.test.js +6 -5
  128. package/test/operations/inners.test.js +17 -14
  129. package/test/operations/invokeFunctions.test.js +2 -2
  130. package/test/operations/isMap.test.js +15 -0
  131. package/test/operations/isMaplike.test.js +15 -0
  132. package/test/operations/json.test.js +2 -2
  133. package/test/operations/keys.test.js +16 -3
  134. package/test/operations/map.test.js +40 -30
  135. package/test/operations/mapExtension.test.js +6 -6
  136. package/test/operations/mapReduce.test.js +14 -12
  137. package/test/operations/mask.test.js +16 -3
  138. package/test/operations/match.test.js +2 -2
  139. package/test/operations/merge.test.js +20 -14
  140. package/test/operations/paginate.test.js +5 -5
  141. package/test/operations/parent.test.js +3 -3
  142. package/test/operations/paths.test.js +20 -27
  143. package/test/operations/plain.test.js +8 -8
  144. package/test/operations/regExpKeys.test.js +22 -18
  145. package/test/operations/reverse.test.js +4 -3
  146. package/test/operations/scope.test.js +6 -5
  147. package/test/operations/set.test.js +11 -0
  148. package/test/operations/shuffle.test.js +3 -2
  149. package/test/operations/sort.test.js +7 -10
  150. package/test/operations/sync.test.js +43 -0
  151. package/test/operations/take.test.js +2 -2
  152. package/test/operations/toFunction.test.js +2 -2
  153. package/test/operations/traverse.test.js +17 -5
  154. package/test/operations/withKeys.test.js +2 -2
  155. package/test/utilities/castArrayLike.test.js +53 -0
  156. package/test/utilities/setParent.test.js +6 -6
  157. package/test/utilities/toFunction.test.js +2 -2
  158. package/test/utilities/toPlainValue.test.js +51 -12
  159. package/src/drivers/DeepMapTree.js +0 -23
  160. package/src/drivers/DeepObjectTree.js +0 -18
  161. package/src/drivers/DeferredTree.js +0 -81
  162. package/src/drivers/FileTree.js +0 -276
  163. package/src/drivers/MapTree.js +0 -70
  164. package/src/drivers/ObjectTree.js +0 -158
  165. package/src/drivers/SetTree.js +0 -34
  166. package/src/drivers/constantTree.js +0 -19
  167. package/src/drivers/limitConcurrency.js +0 -63
  168. package/src/internal.js +0 -16
  169. package/src/utilities/getTreeArgument.js +0 -43
  170. package/test/drivers/DeepMapTree.test.js +0 -17
  171. package/test/drivers/DeepObjectTree.test.js +0 -35
  172. package/test/drivers/DeferredTree.test.js +0 -22
  173. package/test/drivers/FileTree.test.js +0 -192
  174. package/test/drivers/FunctionTree.test.js +0 -46
  175. package/test/drivers/MapTree.test.js +0 -59
  176. package/test/drivers/ObjectTree.test.js +0 -163
  177. package/test/drivers/SetTree.test.js +0 -44
  178. package/test/drivers/constantTree.test.js +0 -13
  179. package/test/drivers/limitConcurrency.test.js +0 -41
  180. package/test/operations/isAsyncMutableTree.test.js +0 -17
  181. package/test/operations/isAsyncTree.test.js +0 -26
  182. package/test/operations/isTreelike.test.js +0 -13
@@ -1,7 +1,9 @@
1
1
  import assert from "node:assert";
2
2
  import { beforeEach, describe, mock, test } from "node:test";
3
- import ExplorableSiteTree from "../../src/drivers/ExplorableSiteTree.js";
4
- import { Tree } from "../../src/internal.js";
3
+ import ExplorableSiteMap from "../../src/drivers/ExplorableSiteMap.js";
4
+ import isMap from "../../src/operations/isMap.js";
5
+ import map from "../../src/operations/map.js";
6
+ import * as trailingSlash from "../../src/trailingSlash.js";
5
7
 
6
8
  const textDecoder = new TextDecoder();
7
9
  const textEncoder = new TextEncoder();
@@ -34,31 +36,35 @@ const mockResponses = {
34
36
  },
35
37
  };
36
38
 
37
- describe("ExplorableSiteTree", () => {
39
+ describe("ExplorableSiteMap", () => {
38
40
  beforeEach(() => {
39
41
  mock.method(global, "fetch", mockFetch);
40
42
  });
41
43
 
42
44
  test("can get the keys of a tree", async () => {
43
- const fixture = new ExplorableSiteTree(mockHost);
44
- const keys = await fixture.keys();
45
- assert.deepEqual(Array.from(keys), ["about/", "index.html"]);
45
+ const fixture = new ExplorableSiteMap(mockHost);
46
+ const keys = [];
47
+ for await (const key of fixture.keys()) {
48
+ keys.push(key);
49
+ }
50
+ assert.deepEqual(keys, ["about/", "index.html"]);
46
51
  });
47
52
 
48
53
  test("can get a plain value for a key", async () => {
49
- const fixture = new ExplorableSiteTree(mockHost);
54
+ const fixture = new ExplorableSiteMap(mockHost);
55
+ /** @type {any} */
50
56
  const arrayBuffer = await fixture.get("index.html");
51
57
  const text = textDecoder.decode(arrayBuffer);
52
58
  assert.equal(text, "Home page");
53
59
  });
54
60
 
55
61
  test("getting an unsupported key returns undefined", async () => {
56
- const fixture = new ExplorableSiteTree(mockHost);
62
+ const fixture = new ExplorableSiteMap(mockHost);
57
63
  assert.equal(await fixture.get("xyz"), undefined);
58
64
  });
59
65
 
60
66
  test("getting a null/undefined key throws an exception", async () => {
61
- const fixture = new ExplorableSiteTree(mockHost);
67
+ const fixture = new ExplorableSiteMap(mockHost);
62
68
  await assert.rejects(async () => {
63
69
  await fixture.get(null);
64
70
  });
@@ -68,20 +74,20 @@ describe("ExplorableSiteTree", () => {
68
74
  });
69
75
 
70
76
  test("can return a new tree for a key that redirects", async () => {
71
- const fixture = new ExplorableSiteTree(mockHost);
77
+ const fixture = new ExplorableSiteMap(mockHost);
72
78
  const about = await fixture.get("about");
73
- assert(about instanceof ExplorableSiteTree);
79
+ assert(about instanceof ExplorableSiteMap);
74
80
  assert.equal(about.href, "https://mock/about/");
75
81
  });
76
82
 
77
83
  test("can convert a site to a plain object", async () => {
78
- const fixture = new ExplorableSiteTree(mockHost);
84
+ const fixture = new ExplorableSiteMap(mockHost);
79
85
  // Convert buffers to strings.
80
- const strings = await Tree.map(fixture, {
86
+ const strings = await map(fixture, {
81
87
  deep: true,
82
88
  value: (value) => textDecoder.decode(value),
83
89
  });
84
- assert.deepEqual(await Tree.plain(strings), {
90
+ assert.deepEqual(await plain(strings), {
85
91
  about: {
86
92
  "Alice.html": "Hello, Alice!",
87
93
  "Bob.html": "Hello, Bob!",
@@ -114,3 +120,12 @@ async function mockFetch(href) {
114
120
  status: 404,
115
121
  };
116
122
  }
123
+
124
+ async function plain(tree) {
125
+ const result = {};
126
+ for await (const [key, value] of tree.entries()) {
127
+ const normalized = trailingSlash.remove(key);
128
+ result[normalized] = isMap(value) ? await plain(value) : value;
129
+ }
130
+ return result;
131
+ }
@@ -0,0 +1,156 @@
1
+ import assert from "node:assert";
2
+ import * as fs from "node:fs";
3
+ import path from "node:path";
4
+ import { describe, test } from "node:test";
5
+ import { fileURLToPath } from "node:url";
6
+ import FileMap from "../../src/drivers/FileMap.js";
7
+
8
+ const dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const tempDirectory = path.join(dirname, "fixtures/temp");
10
+
11
+ const textDecoder = new TextDecoder();
12
+
13
+ describe("FileMap", () => {
14
+ test("can get the keys of the tree", () => {
15
+ const fixture = createFixture("fixtures/markdown");
16
+ assert.deepEqual(Array.from(fixture.keys()), [
17
+ "Alice.md",
18
+ "Bob.md",
19
+ "Carol.md",
20
+ "subfolder/",
21
+ ]);
22
+ });
23
+
24
+ test("can get the value for a key", () => {
25
+ const fixture = createFixture("fixtures/markdown");
26
+ const buffer = fixture.get("Alice.md");
27
+ const text = textDecoder.decode(buffer);
28
+ assert.equal(text, "Hello, **Alice**.");
29
+ });
30
+
31
+ test("getting an unsupported key returns undefined", () => {
32
+ const fixture = createFixture("fixtures/markdown");
33
+ assert.equal(fixture.get("xyz"), undefined);
34
+ });
35
+
36
+ test("getting empty key returns undefined", () => {
37
+ const fixture = createFixture("fixtures/markdown");
38
+ assert.equal(fixture.get(""), undefined);
39
+ });
40
+
41
+ test("getting a null/undefined key throws an exception", async () => {
42
+ const fixture = createFixture("fixtures/markdown");
43
+ await assert.rejects(async () => {
44
+ await fixture.get(null);
45
+ });
46
+ await assert.rejects(async () => {
47
+ await fixture.get(undefined);
48
+ });
49
+ });
50
+
51
+ test("can retrieve values with optional trailing slash", () => {
52
+ const fixture = createFixture("fixtures/markdown");
53
+ assert(fixture.get("Alice.md"));
54
+ assert(fixture.get("Alice.md/"));
55
+ assert(fixture.get("subfolder"));
56
+ assert(fixture.get("subfolder/"));
57
+ });
58
+
59
+ test("sets parent on subtrees", () => {
60
+ const fixture = createFixture("fixtures");
61
+ const markdown = fixture.get("markdown");
62
+ assert.deepEqual(markdown.parent, fixture);
63
+ });
64
+
65
+ test("can write out a file via set()", () => {
66
+ createTempDirectory();
67
+
68
+ // Write out a file.
69
+ const fileName = "file1";
70
+ const fileText = "This is the first file.";
71
+ const tempFiles = new FileMap(tempDirectory);
72
+ tempFiles.set(fileName, fileText);
73
+
74
+ // Read it back in.
75
+ const filePath = path.join(tempDirectory, fileName);
76
+ const actualText = String(fs.readFileSync(filePath));
77
+
78
+ assert.equal(fileText, actualText);
79
+
80
+ removeTempDirectory();
81
+ });
82
+
83
+ test("create subfolder via child()", () => {
84
+ createTempDirectory();
85
+
86
+ // Write out new, empty folder called "empty".
87
+ const tempFiles = new FileMap(tempDirectory);
88
+ tempFiles.child("empty");
89
+
90
+ // Verify folder exists and has no contents.
91
+ const folderPath = path.join(tempDirectory, "empty");
92
+ const stats = fs.statSync(folderPath);
93
+ assert(stats.isDirectory());
94
+ const files = fs.readdirSync(folderPath);
95
+ assert.deepEqual(files, []);
96
+
97
+ removeTempDirectory();
98
+ });
99
+
100
+ test("can delete a file", () => {
101
+ createTempDirectory();
102
+ const tempFile = path.join(tempDirectory, "file");
103
+ fs.writeFileSync(tempFile, "");
104
+ const tempFiles = new FileMap(tempDirectory);
105
+ const deleted = tempFiles.delete("file");
106
+ assert(deleted);
107
+ let stats;
108
+ try {
109
+ stats = fs.statSync(tempFile);
110
+ } catch (/** @type {any} */ error) {
111
+ if (error.code !== "ENOENT") {
112
+ throw error;
113
+ }
114
+ }
115
+ assert(stats === undefined);
116
+ removeTempDirectory();
117
+ });
118
+
119
+ test("delete returns false if file does not exist", () => {
120
+ createTempDirectory();
121
+ const tempFiles = new FileMap(tempDirectory);
122
+ const deleted = tempFiles.delete("nonexistent-file");
123
+ assert(!deleted);
124
+ removeTempDirectory();
125
+ });
126
+
127
+ test("can delete a folder", () => {
128
+ createTempDirectory();
129
+ const folder = path.join(tempDirectory, "folder");
130
+ fs.mkdirSync(folder);
131
+ const tempFiles = new FileMap(tempDirectory);
132
+ tempFiles.delete("folder");
133
+ let stats;
134
+ try {
135
+ stats = fs.statSync(folder);
136
+ } catch (/** @type {any} */ error) {
137
+ if (error.code !== "ENOENT") {
138
+ throw error;
139
+ }
140
+ }
141
+ assert(stats === undefined);
142
+ removeTempDirectory();
143
+ });
144
+ });
145
+
146
+ function createFixture(fixturePath) {
147
+ return new FileMap(path.join(dirname, fixturePath));
148
+ }
149
+
150
+ function createTempDirectory() {
151
+ fs.mkdirSync(tempDirectory, { recursive: true });
152
+ }
153
+
154
+ function removeTempDirectory() {
155
+ fs.rmSync(tempDirectory, { force: true, recursive: true });
156
+ }
@@ -0,0 +1,56 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import FunctionMap from "../../src/drivers/FunctionMap.js";
4
+ import * as symbols from "../../src/symbols.js";
5
+
6
+ describe("FunctionMap", () => {
7
+ test("keys uses supplied domain", () => {
8
+ const map = createFixture();
9
+ assert.deepEqual(Array.from(map.keys()), [
10
+ "Alice.md",
11
+ "Bob.md",
12
+ "Carol.md",
13
+ ]);
14
+ });
15
+
16
+ test("can get the value for a key", () => {
17
+ const map = createFixture();
18
+ const alice = map.get("Alice.md");
19
+ assert.equal(alice, "Hello, **Alice**.");
20
+ });
21
+
22
+ test("can get the value for an async function", async () => {
23
+ const map = new FunctionMap(async (s) => ({ s }));
24
+ const result = await map.get("test");
25
+ assert.deepEqual(result, {
26
+ s: "test",
27
+ });
28
+ assert.equal(result[symbols.parent], map);
29
+ });
30
+
31
+ test("getting a value from function with multiple arguments curries the function", () => {
32
+ const map = new FunctionMap((a, b, c) => a + b + c);
33
+ const fnA = map.get(1);
34
+ const fnAB = fnA.get(2);
35
+ const result = fnAB.get(3);
36
+ assert.equal(result, 6);
37
+ });
38
+
39
+ test("getting an unsupported key returns undefined", () => {
40
+ const fixture = createFixture();
41
+ assert.equal(fixture.get("xyz"), undefined);
42
+ });
43
+ });
44
+
45
+ function createFixture() {
46
+ return new FunctionMap(
47
+ (key) => {
48
+ if (key?.endsWith?.(".md")) {
49
+ const name = key.slice(0, -3);
50
+ return `Hello, **${name}**.`;
51
+ }
52
+ return undefined;
53
+ },
54
+ ["Alice.md", "Bob.md", "Carol.md"]
55
+ );
56
+ }
@@ -0,0 +1,194 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import ObjectMap from "../../src/drivers/ObjectMap.js";
4
+ import plain from "../../src/operations/plain.js";
5
+ import * as symbols from "../../src/symbols.js";
6
+
7
+ describe("ObjectMap", () => {
8
+ test("can get the keys of the tree", () => {
9
+ const fixture = createFixture();
10
+ assert.deepEqual(Array.from(fixture.keys()), [
11
+ "Alice.md",
12
+ "Bob.md",
13
+ "Carol.md",
14
+ ]);
15
+ });
16
+
17
+ test("can get the value for a key", () => {
18
+ const map = createFixture();
19
+ const alice = map.get("Alice.md");
20
+ assert.equal(alice, "Hello, **Alice**.");
21
+ });
22
+
23
+ test("getting an unsupported key returns undefined", () => {
24
+ const map = createFixture();
25
+ assert.equal(map.get("xyz"), undefined);
26
+ });
27
+
28
+ test("getting a null/undefined key throws an exception", async () => {
29
+ const map = createFixture();
30
+ await assert.rejects(async () => {
31
+ map.get(null);
32
+ });
33
+ await assert.rejects(async () => {
34
+ map.get(undefined);
35
+ });
36
+ });
37
+
38
+ test("can set a value", () => {
39
+ const map = new ObjectMap({
40
+ a: 1,
41
+ b: 2,
42
+ c: 3,
43
+ });
44
+
45
+ // Update existing key
46
+ map.set("a", 4);
47
+
48
+ // Delete key
49
+ map.delete("b");
50
+
51
+ // Overwrite key with trailing slash
52
+ map.set("c/", {});
53
+
54
+ // New key
55
+ map.set("d", 5);
56
+
57
+ const entries = Array.from(map.entries());
58
+ assert.deepEqual(entries, [
59
+ ["a", 4],
60
+ ["c/", {}],
61
+ ["d", 5],
62
+ ]);
63
+ });
64
+
65
+ test("can wrap a class instance", () => {
66
+ class Foo {
67
+ constructor() {
68
+ this.a = 1;
69
+ }
70
+
71
+ get prop() {
72
+ return this._prop;
73
+ }
74
+ set prop(prop) {
75
+ this._prop = prop;
76
+ }
77
+ }
78
+ class Bar extends Foo {
79
+ method() {}
80
+ }
81
+ const bar = new Bar();
82
+ /** @type {any} */ (bar).extra = "Hello";
83
+
84
+ const map = new ObjectMap(bar);
85
+ const entries = Array.from(map.entries());
86
+ assert.deepEqual(entries, [
87
+ ["a", 1],
88
+ ["extra", "Hello"],
89
+ ["prop", undefined],
90
+ ]);
91
+ assert.equal(map.get("a"), 1);
92
+ map.set("prop", "Goodbye");
93
+ assert.equal(bar.prop, "Goodbye");
94
+ assert.equal(map.get("prop"), "Goodbye");
95
+ });
96
+
97
+ test("sets parent symbol on subobjects", () => {
98
+ const map = new ObjectMap({
99
+ sub: {},
100
+ });
101
+ const sub = map.get("sub");
102
+ assert.equal(sub[symbols.parent], map);
103
+ });
104
+
105
+ test("sets parent on subtrees", () => {
106
+ const map = new ObjectMap({
107
+ a: 1,
108
+ more: new ObjectMap({
109
+ b: 2,
110
+ }),
111
+ });
112
+ const more = map.get("more");
113
+ assert.equal(more.parent, map);
114
+ });
115
+
116
+ test("adds trailing slashes to keys for subtrees", () => {
117
+ const map = new ObjectMap({
118
+ a1: 1,
119
+ a2: new ObjectMap({
120
+ b1: 2,
121
+ }),
122
+ a3: 3,
123
+ a4: new ObjectMap({
124
+ b2: 4,
125
+ }),
126
+ });
127
+ const keys = Array.from(map.keys());
128
+ assert.deepEqual(keys, ["a1", "a2/", "a3", "a4/"]);
129
+ });
130
+
131
+ test("can retrieve values with optional trailing slash", () => {
132
+ const subMap = {
133
+ get(key) {},
134
+ keys() {},
135
+ };
136
+ const map = new ObjectMap({
137
+ a: 1,
138
+ subMap,
139
+ });
140
+ assert.equal(map.get("a"), 1);
141
+ assert.equal(map.get("a/"), 1);
142
+ assert.equal(map.get("subMap"), subMap);
143
+ assert.equal(map.get("subMap/"), subMap);
144
+ });
145
+
146
+ test("method on an object is bound to the object", () => {
147
+ const n = new Number(123);
148
+ const map = new ObjectMap(n);
149
+ const method = map.get("toString");
150
+ assert.equal(method(), "123");
151
+ });
152
+
153
+ test("defers to symbols.keys for keys if defined", () => {
154
+ const map = new ObjectMap({
155
+ [symbols.keys]: () => ["a", "b", "c"],
156
+ });
157
+ assert.deepEqual(Array.from(map.keys()), ["a", "b", "c"]);
158
+ });
159
+
160
+ test("deep option treats sub-objects and sub-arrays as child nodes", async () => {
161
+ const map = new ObjectMap(
162
+ {
163
+ a: 1,
164
+ object: {
165
+ b: 2,
166
+ },
167
+ array: [3],
168
+ },
169
+ { deep: true }
170
+ );
171
+
172
+ // Adds trailing slashes to keys for sub-objects and sub-arrays
173
+ const keys = Array.from(await map.keys());
174
+ assert.deepEqual(keys, ["a", "object/", "array/"]);
175
+
176
+ const object = await map.get("object");
177
+ assert.equal(object instanceof ObjectMap, true);
178
+ assert.deepEqual(await plain(object), { b: 2 });
179
+ assert.equal(object.parent, map);
180
+
181
+ const array = await map.get("array");
182
+ assert.equal(array instanceof ObjectMap, true);
183
+ assert.deepEqual(await plain(array), [3]);
184
+ assert.equal(array.parent, map);
185
+ });
186
+ });
187
+
188
+ function createFixture() {
189
+ return new ObjectMap({
190
+ "Alice.md": "Hello, **Alice**.",
191
+ "Bob.md": "Hello, **Bob**.",
192
+ "Carol.md": "Hello, **Carol**.",
193
+ });
194
+ }
@@ -0,0 +1,35 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import SetMap from "../../src/drivers/SetMap.js";
4
+
5
+ describe("SetMap", () => {
6
+ test("can get the keys of the map", async () => {
7
+ const set = new Set(["a", "b", "c"]);
8
+ const fixture = new SetMap(set);
9
+ assert.deepEqual(Array.from(await fixture.keys()), ["a", "b", "c"]);
10
+ });
11
+
12
+ test("can get the value for a key", async () => {
13
+ const set = new Set(["a", "b", "c"]);
14
+ const fixture = new SetMap(set);
15
+ const a = await fixture.get("a");
16
+ assert.equal(a, "a");
17
+ });
18
+
19
+ test("getting an unsupported key returns undefined", async () => {
20
+ const set = new Set(["a", "b", "c"]);
21
+ const fixture = new SetMap(set);
22
+ assert.equal(await fixture.get("d"), undefined);
23
+ });
24
+
25
+ test("getting a null/undefined key throws an exception", async () => {
26
+ const set = new Set(["a", "b", "c"]);
27
+ const fixture = new SetMap(set);
28
+ await assert.rejects(async () => {
29
+ await fixture.get(null);
30
+ });
31
+ await assert.rejects(async () => {
32
+ await fixture.get(undefined);
33
+ });
34
+ });
35
+ });
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import { beforeEach, describe, mock, test } from "node:test";
3
- import SiteTree from "../../src/drivers/SiteTree.js";
3
+ import SiteMap from "../../src/drivers/SiteMap.js";
4
4
 
5
5
  const textDecoder = new TextDecoder();
6
6
  const textEncoder = new TextEncoder();
@@ -27,38 +27,42 @@ const mockResponses = {
27
27
  },
28
28
  };
29
29
 
30
- describe("SiteTree", () => {
30
+ describe("SiteMap", () => {
31
31
  beforeEach(() => {
32
32
  mock.method(global, "fetch", mockFetch);
33
33
  });
34
34
 
35
35
  test("returns an empty array as the keys of a tree", async () => {
36
- const fixture = new SiteTree(mockHost);
37
- const keys = await fixture.keys();
38
- assert.deepEqual(Array.from(keys), []);
36
+ const fixture = new SiteMap(mockHost);
37
+ const keys = [];
38
+ for await (const key of fixture.keys()) {
39
+ keys.push(key);
40
+ }
41
+ assert.deepEqual(keys, []);
39
42
  });
40
43
 
41
44
  test("can get a plain value for a key", async () => {
42
- const fixture = new SiteTree(mockHost);
45
+ const fixture = new SiteMap(mockHost);
46
+ /** @type {any} */
43
47
  const arrayBuffer = await fixture.get("index.html");
44
48
  const text = textDecoder.decode(arrayBuffer);
45
49
  assert.equal(text, "Home page");
46
50
  });
47
51
 
48
52
  test("immediately return a new tree for a key with a trailing slash", async () => {
49
- const fixture = new SiteTree(mockHost);
53
+ const fixture = new SiteMap(mockHost);
50
54
  const about = await fixture.get("about/");
51
- assert(about instanceof SiteTree);
55
+ assert(about instanceof SiteMap);
52
56
  assert.equal(about.href, "https://mock/about/");
53
57
  });
54
58
 
55
59
  test("getting an unsupported key returns undefined", async () => {
56
- const fixture = new SiteTree(mockHost);
60
+ const fixture = new SiteMap(mockHost);
57
61
  assert.equal(await fixture.get("xyz"), undefined);
58
62
  });
59
63
 
60
64
  test("getting a null/undefined key throws an exception", async () => {
61
- const fixture = new SiteTree(mockHost);
65
+ const fixture = new SiteMap(mockHost);
62
66
  await assert.rejects(async () => {
63
67
  await fixture.get(null);
64
68
  });