@weborigami/async-tree 0.5.3 → 0.5.5

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 (159) hide show
  1. package/index.ts +16 -6
  2. package/package.json +2 -2
  3. package/shared.js +20 -30
  4. package/src/Tree.js +62 -502
  5. package/src/constants.js +2 -0
  6. package/src/drivers/BrowserFileTree.js +9 -10
  7. package/src/drivers/DeepMapTree.js +3 -3
  8. package/src/drivers/DeepObjectTree.js +4 -5
  9. package/src/drivers/DeferredTree.js +2 -2
  10. package/src/drivers/FileTree.js +11 -33
  11. package/src/drivers/FunctionTree.js +1 -1
  12. package/src/drivers/MapTree.js +6 -6
  13. package/src/drivers/ObjectTree.js +4 -3
  14. package/src/drivers/SetTree.js +1 -1
  15. package/src/drivers/SiteTree.js +1 -1
  16. package/src/drivers/constantTree.js +1 -1
  17. package/src/extension.js +5 -3
  18. package/src/jsonKeys.js +5 -7
  19. package/src/operations/addNextPrevious.js +10 -9
  20. package/src/operations/assign.js +40 -0
  21. package/src/operations/cache.js +18 -12
  22. package/src/operations/cachedKeyFunctions.js +15 -4
  23. package/src/operations/clear.js +20 -0
  24. package/src/operations/concat.js +17 -0
  25. package/src/operations/deepMap.js +25 -0
  26. package/src/operations/deepMerge.js +11 -25
  27. package/src/operations/deepReverse.js +6 -7
  28. package/src/operations/deepTake.js +6 -7
  29. package/src/operations/deepText.js +4 -4
  30. package/src/operations/deepValuesIterator.js +8 -6
  31. package/src/operations/defineds.js +32 -0
  32. package/src/operations/delete.js +20 -0
  33. package/src/operations/entries.js +16 -0
  34. package/src/operations/extensionKeyFunctions.js +1 -1
  35. package/src/operations/filter.js +7 -8
  36. package/src/operations/first.js +18 -0
  37. package/src/operations/forEach.js +20 -0
  38. package/src/operations/from.js +77 -0
  39. package/src/operations/fromFn.js +26 -0
  40. package/src/operations/globKeys.js +8 -8
  41. package/src/operations/group.js +9 -7
  42. package/src/operations/has.js +16 -0
  43. package/src/operations/indent.js +4 -2
  44. package/src/operations/inners.js +29 -0
  45. package/src/operations/invokeFunctions.js +5 -4
  46. package/src/operations/isAsyncMutableTree.js +15 -0
  47. package/src/operations/isAsyncTree.js +21 -0
  48. package/src/operations/isTraversable.js +15 -0
  49. package/src/operations/isTreelike.js +33 -0
  50. package/src/operations/json.js +4 -3
  51. package/src/operations/keys.js +14 -0
  52. package/src/operations/length.js +15 -0
  53. package/src/operations/map.js +157 -95
  54. package/src/operations/mapExtension.js +27 -0
  55. package/src/operations/mapReduce.js +44 -0
  56. package/src/operations/mask.js +18 -16
  57. package/src/operations/match.js +74 -0
  58. package/src/operations/merge.js +22 -20
  59. package/src/operations/paginate.js +3 -5
  60. package/src/operations/parent.js +13 -0
  61. package/src/operations/paths.js +51 -0
  62. package/src/operations/plain.js +34 -0
  63. package/src/operations/regExpKeys.js +4 -5
  64. package/src/operations/remove.js +14 -0
  65. package/src/operations/reverse.js +4 -6
  66. package/src/operations/root.js +17 -0
  67. package/src/operations/scope.js +4 -6
  68. package/src/operations/setDeep.js +50 -0
  69. package/src/operations/shuffle.js +46 -0
  70. package/src/operations/sort.js +19 -12
  71. package/src/operations/take.js +3 -5
  72. package/src/operations/text.js +3 -3
  73. package/src/operations/toFunction.js +14 -0
  74. package/src/operations/traverse.js +25 -0
  75. package/src/operations/traverseOrThrow.js +64 -0
  76. package/src/operations/traversePath.js +16 -0
  77. package/src/operations/values.js +15 -0
  78. package/src/operations/withKeys.js +33 -0
  79. package/src/utilities/TypedArray.js +2 -0
  80. package/src/utilities/box.js +20 -0
  81. package/src/utilities/castArraylike.js +38 -0
  82. package/src/utilities/getParent.js +33 -0
  83. package/src/utilities/getRealmObjectPrototype.js +19 -0
  84. package/src/utilities/getTreeArgument.js +43 -0
  85. package/src/utilities/isPacked.js +20 -0
  86. package/src/utilities/isPlainObject.js +29 -0
  87. package/src/utilities/isPrimitive.js +13 -0
  88. package/src/utilities/isStringlike.js +25 -0
  89. package/src/utilities/isUnpackable.js +13 -0
  90. package/src/utilities/keysFromPath.js +34 -0
  91. package/src/utilities/naturalOrder.js +9 -0
  92. package/src/utilities/pathFromKeys.js +18 -0
  93. package/src/utilities/setParent.js +38 -0
  94. package/src/utilities/toFunction.js +41 -0
  95. package/src/utilities/toPlainValue.js +95 -0
  96. package/src/utilities/toString.js +37 -0
  97. package/test/drivers/ExplorableSiteTree.test.js +1 -1
  98. package/test/drivers/FileTree.test.js +1 -1
  99. package/test/drivers/calendarTree.test.js +1 -1
  100. package/test/jsonKeys.test.js +1 -1
  101. package/test/operations/assign.test.js +54 -0
  102. package/test/operations/cache.test.js +1 -1
  103. package/test/operations/cachedKeyFunctions.test.js +16 -16
  104. package/test/operations/clear.test.js +34 -0
  105. package/test/operations/deepMap.test.js +29 -0
  106. package/test/operations/deepMerge.test.js +2 -6
  107. package/test/operations/deepReverse.test.js +1 -1
  108. package/test/operations/defineds.test.js +25 -0
  109. package/test/operations/delete.test.js +20 -0
  110. package/test/operations/entries.test.js +18 -0
  111. package/test/operations/extensionKeyFunctions.test.js +10 -10
  112. package/test/operations/first.test.js +15 -0
  113. package/test/operations/fixtures/README.md +1 -0
  114. package/test/operations/forEach.test.js +22 -0
  115. package/test/operations/from.test.js +67 -0
  116. package/test/operations/globKeys.test.js +3 -3
  117. package/test/operations/has.test.js +15 -0
  118. package/test/operations/inners.test.js +30 -0
  119. package/test/operations/invokeFunctions.test.js +1 -1
  120. package/test/operations/isAsyncMutableTree.test.js +17 -0
  121. package/test/operations/isAsyncTree.test.js +26 -0
  122. package/test/operations/isTreelike.test.js +13 -0
  123. package/test/operations/keys.test.js +15 -0
  124. package/test/operations/length.test.js +15 -0
  125. package/test/operations/map.test.js +62 -45
  126. package/test/operations/mapExtension.test.js +0 -0
  127. package/test/operations/mapReduce.test.js +23 -0
  128. package/test/operations/mask.test.js +1 -1
  129. package/test/operations/match.test.js +33 -0
  130. package/test/operations/merge.test.js +23 -9
  131. package/test/operations/paginate.test.js +2 -1
  132. package/test/operations/parent.test.js +15 -0
  133. package/test/operations/paths.test.js +40 -0
  134. package/test/operations/plain.test.js +69 -0
  135. package/test/operations/reverse.test.js +1 -1
  136. package/test/operations/scope.test.js +1 -1
  137. package/test/operations/setDeep.test.js +53 -0
  138. package/test/operations/shuffle.test.js +18 -0
  139. package/test/operations/sort.test.js +3 -3
  140. package/test/operations/toFunction.test.js +16 -0
  141. package/test/operations/traverse.test.js +43 -0
  142. package/test/operations/traversePath.test.js +16 -0
  143. package/test/operations/values.test.js +18 -0
  144. package/test/operations/withKeys.test.js +21 -0
  145. package/test/utilities/box.test.js +26 -0
  146. package/test/utilities/getRealmObjectPrototype.test.js +11 -0
  147. package/test/utilities/isPlainObject.test.js +13 -0
  148. package/test/utilities/keysFromPath.test.js +14 -0
  149. package/test/utilities/naturalOrder.test.js +11 -0
  150. package/test/utilities/pathFromKeys.test.js +12 -0
  151. package/test/utilities/setParent.test.js +34 -0
  152. package/test/utilities/toFunction.test.js +34 -0
  153. package/test/utilities/toPlainValue.test.js +27 -0
  154. package/test/utilities/toString.test.js +22 -0
  155. package/src/Tree.d.ts +0 -24
  156. package/src/utilities.d.ts +0 -21
  157. package/src/utilities.js +0 -439
  158. package/test/Tree.test.js +0 -407
  159. package/test/utilities.test.js +0 -141
@@ -1,20 +1,17 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
- import FunctionTree from "../../src/drivers/FunctionTree.js";
4
3
  import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
5
4
  import map from "../../src/operations/map.js";
6
5
  import * as trailingSlash from "../../src/trailingSlash.js";
7
6
 
8
7
  describe("map", () => {
9
- test("returns identity graph if no key or value function is supplied", async () => {
8
+ test("throws if no key or value function is supplied", async () => {
10
9
  const tree = {
11
10
  a: "letter a",
12
11
  b: "letter b",
13
12
  };
14
- const mapped = map(tree, {});
15
- assert.deepEqual(await Tree.plain(mapped), {
16
- a: "letter a",
17
- b: "letter b",
13
+ assert.rejects(async () => {
14
+ await map(tree, {});
18
15
  });
19
16
  });
20
17
 
@@ -24,7 +21,7 @@ describe("map", () => {
24
21
  b: "letter b",
25
22
  c: undefined, // Won't be mapped
26
23
  });
27
- const mapped = map(tree, {
24
+ const mapped = await map(tree, {
28
25
  value: (sourceValue, sourceKey, innerTree) => {
29
26
  assert(sourceKey === "a" || sourceKey === "b");
30
27
  assert.equal(innerTree, tree);
@@ -43,7 +40,7 @@ describe("map", () => {
43
40
  a: "letter a",
44
41
  b: "letter b",
45
42
  };
46
- const uppercaseValues = map(tree, (sourceValue, sourceKey, tree) => {
43
+ const uppercaseValues = await map(tree, (sourceValue, sourceKey, tree) => {
47
44
  assert(sourceKey === "a" || sourceKey === "b");
48
45
  return sourceValue.toUpperCase();
49
46
  });
@@ -58,7 +55,7 @@ describe("map", () => {
58
55
  a: "letter a",
59
56
  b: "letter b",
60
57
  };
61
- const underscoreKeys = map(tree, {
58
+ const underscoreKeys = await map(tree, {
62
59
  key: addUnderscore,
63
60
  inverseKey: removeUnderscore,
64
61
  });
@@ -73,7 +70,7 @@ describe("map", () => {
73
70
  a: "letter a",
74
71
  b: "letter b",
75
72
  };
76
- const underscoreKeys = map(tree, {
73
+ const underscoreKeys = await map(tree, {
77
74
  key: addUnderscore,
78
75
  });
79
76
  assert.deepEqual(await Tree.plain(underscoreKeys), {
@@ -83,18 +80,19 @@ describe("map", () => {
83
80
  });
84
81
 
85
82
  test("maps keys and values", async () => {
86
- const tree = {
87
- a: "letter a",
88
- b: "letter b",
89
- };
90
- const underscoreKeysUppercaseValues = map(tree, {
91
- key: addUnderscore,
92
- inverseKey: removeUnderscore,
93
- value: async (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
83
+ const treelike = new ObjectTree([
84
+ { name: "Alice", age: 1 },
85
+ { name: "Bob", age: 2 },
86
+ { name: "Carol", age: 3 },
87
+ ]);
88
+ const result = await map(treelike, {
89
+ key: (value, key, tree) => value.name,
90
+ value: (value, key, tree) => value.age,
94
91
  });
95
- assert.deepEqual(await Tree.plain(underscoreKeysUppercaseValues), {
96
- _a: "LETTER A",
97
- _b: "LETTER B",
92
+ assert.deepEqual(await Tree.plain(result), {
93
+ Alice: 1,
94
+ Bob: 2,
95
+ Carol: 3,
98
96
  });
99
97
  });
100
98
 
@@ -105,8 +103,8 @@ describe("map", () => {
105
103
  b: "letter b",
106
104
  },
107
105
  };
108
- const underscoreKeys = map(tree, {
109
- key: async (sourceKey, tree) => `_${sourceKey}`,
106
+ const underscoreKeys = await map(tree, {
107
+ key: async (value, sourceKey, tree) => `_${sourceKey}`,
110
108
  inverseKey: async (resultKey, tree) => resultKey.slice(1),
111
109
  value: async (sourceValue, sourceKey, tree) => sourceKey,
112
110
  });
@@ -124,7 +122,7 @@ describe("map", () => {
124
122
  a: "letter a",
125
123
  b: "letter b",
126
124
  };
127
- const mapped = map(tree, uppercase);
125
+ const mapped = await map(tree, uppercase);
128
126
  assert.deepEqual(await Tree.plain(mapped), {
129
127
  _a: "LETTER A",
130
128
  _b: "LETTER B",
@@ -138,7 +136,7 @@ describe("map", () => {
138
136
  b: "letter b",
139
137
  },
140
138
  });
141
- const uppercaseValues = map(tree, {
139
+ const uppercaseValues = await map(tree, {
142
140
  deep: true,
143
141
  value: (sourceValue, sourceKey, tree) => sourceValue.toUpperCase(),
144
142
  });
@@ -157,7 +155,7 @@ describe("map", () => {
157
155
  b: "letter b",
158
156
  },
159
157
  });
160
- const underscoreKeys = map(tree, {
158
+ const underscoreKeys = await map(tree, {
161
159
  deep: true,
162
160
  key: addUnderscore,
163
161
  inverseKey: removeUnderscore,
@@ -177,7 +175,7 @@ describe("map", () => {
177
175
  b: "letter b",
178
176
  },
179
177
  });
180
- const underscoreKeysUppercaseValues = map(tree, {
178
+ const underscoreKeysUppercaseValues = await map(tree, {
181
179
  deep: true,
182
180
  key: addUnderscore,
183
181
  inverseKey: removeUnderscore,
@@ -191,6 +189,42 @@ describe("map", () => {
191
189
  });
192
190
  });
193
191
 
192
+ test("keyNeedsSourceValue can be set to false in cases where the value isn't necessary", async () => {
193
+ const tree = {
194
+ a: "letter a",
195
+ b: "letter b",
196
+ c: "letter c",
197
+ };
198
+ const mapped = await map(tree, {
199
+ keyNeedsSourceValue: false,
200
+ key: (value, key) => {
201
+ assert.equal(value, null);
202
+ return key.toUpperCase();
203
+ },
204
+ });
205
+ assert.deepEqual(await Tree.plain(mapped), {
206
+ A: "letter a",
207
+ B: "letter b",
208
+ C: "letter c",
209
+ });
210
+ });
211
+
212
+ test("can add an extension to a key", async () => {
213
+ const treelike = {
214
+ "file0.txt": 1,
215
+ file1: 2,
216
+ file2: 3,
217
+ };
218
+ const fixture = await map(treelike, {
219
+ extension: "->.data",
220
+ });
221
+ assert.deepEqual(await Tree.plain(fixture), {
222
+ "file0.txt.data": 1,
223
+ "file1.data": 2,
224
+ "file2.data": 3,
225
+ });
226
+ });
227
+
194
228
  test("can change a key's extension", async () => {
195
229
  const treelike = {
196
230
  "file1.lower": "will be mapped",
@@ -224,26 +258,9 @@ describe("map", () => {
224
258
  },
225
259
  });
226
260
  });
227
-
228
- test("needsSourceValue can be set to false in cases where the value isn't necessary", async () => {
229
- let flag = false;
230
- const tree = new FunctionTree(() => {
231
- flag = true;
232
- }, ["a", "b", "c"]);
233
- const mapped = map(tree, {
234
- needsSourceValue: false,
235
- value: () => "X",
236
- });
237
- assert.deepEqual(await Tree.plain(mapped), {
238
- a: "X",
239
- b: "X",
240
- c: "X",
241
- });
242
- assert(!flag);
243
- });
244
261
  });
245
262
 
246
- function addUnderscore(key) {
263
+ function addUnderscore(value, key) {
247
264
  return `_${key}`;
248
265
  }
249
266
 
File without changes
@@ -0,0 +1,23 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import DeepObjectTree from "../../src/drivers/DeepObjectTree.js";
4
+ import mapReduce from "../../src/operations/mapReduce.js";
5
+
6
+ describe("mapReduce", () => {
7
+ test("can map values and reduce them", async () => {
8
+ const tree = new DeepObjectTree({
9
+ a: 1,
10
+ b: 2,
11
+ more: {
12
+ c: 3,
13
+ },
14
+ d: 4,
15
+ });
16
+ const reduced = await mapReduce(
17
+ tree,
18
+ (value) => value,
19
+ async (values) => String.prototype.concat(...values)
20
+ );
21
+ assert.deepEqual(reduced, "1234");
22
+ });
23
+ });
@@ -5,7 +5,7 @@ import mask from "../../src/operations/mask.js";
5
5
 
6
6
  describe("mask", () => {
7
7
  test("removes keys and values whose mask values are falsy", async () => {
8
- const result = mask(
8
+ const result = await mask(
9
9
  {
10
10
  a: 1,
11
11
  b: 2,
@@ -0,0 +1,33 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import { Tree } from "../../src/internal.js";
4
+ import match from "../../src/operations/match.js";
5
+
6
+ describe("match", () => {
7
+ test("matches keys against a simplified pattern", async () => {
8
+ function fn(matches) {
9
+ return `Hello, ${matches.name}!`;
10
+ }
11
+ const tree = match("[name].html", fn, [
12
+ "Alice.html",
13
+ "Bob.html",
14
+ "Carol.html",
15
+ ]);
16
+ assert.deepEqual(await Tree.plain(tree), {
17
+ "Alice.html": "Hello, Alice!",
18
+ "Bob.html": "Hello, Bob!",
19
+ "Carol.html": "Hello, Carol!",
20
+ });
21
+ const value = await tree.get("David.html");
22
+ assert.equal(value, "Hello, David!");
23
+ });
24
+
25
+ test("matches keys against a regular expression", async () => {
26
+ function fn(matches) {
27
+ return `Hello, ${matches.name}!`;
28
+ }
29
+ const tree = match(/^(?<name>.+)\.html$/, fn);
30
+ const value = await tree.get("Alice.html");
31
+ assert.equal(value, "Hello, Alice!");
32
+ });
33
+ });
@@ -2,26 +2,25 @@ import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
3
  import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
4
4
  import merge from "../../src/operations/merge.js";
5
- import * as symbols from "../../src/symbols.js";
6
5
 
7
6
  describe("merge", () => {
8
7
  test("performs a shallow merge", async () => {
9
8
  const fixture = merge(
10
- {
9
+ new ObjectTree({
11
10
  a: 1,
12
11
  // Will be obscured by `b` that follows
13
12
  b: {
14
13
  c: 2,
15
14
  },
16
- },
17
- {
15
+ }),
16
+ new ObjectTree({
18
17
  b: {
19
18
  d: 3,
20
19
  },
21
20
  e: {
22
21
  f: 4,
23
22
  },
24
- }
23
+ })
25
24
  );
26
25
 
27
26
  assert.deepEqual(await Tree.plain(fixture), {
@@ -37,10 +36,6 @@ describe("merge", () => {
37
36
  // Merge is shallow, and last tree wins, so `b/c` doesn't exist
38
37
  const c = await Tree.traverse(fixture, "b", "c");
39
38
  assert.equal(c, undefined);
40
-
41
- // Parent of a subvalue is the merged tree
42
- const b = await fixture.get("b");
43
- assert.equal(b[symbols.parent], fixture);
44
39
  });
45
40
 
46
41
  test("subtree can overwrite a leaf node", async () => {
@@ -61,4 +56,23 @@ describe("merge", () => {
61
56
  },
62
57
  });
63
58
  });
59
+
60
+ test("if all arguments are plain objects, result is a plain object", async () => {
61
+ const result = await merge(
62
+ {
63
+ a: 1,
64
+ b: 2,
65
+ },
66
+ {
67
+ c: 3,
68
+ d: 4,
69
+ }
70
+ );
71
+ assert.deepEqual(result, {
72
+ a: 1,
73
+ b: 2,
74
+ c: 3,
75
+ d: 4,
76
+ });
77
+ });
64
78
  });
@@ -13,7 +13,8 @@ describe("paginate", () => {
13
13
  e: 5,
14
14
  };
15
15
  const paginated = await paginate.call(null, treelike, 2);
16
- assert.deepEqual(await Tree.plain(paginated), {
16
+ const plain = await Tree.plain(paginated);
17
+ assert.deepEqual(await plain, {
17
18
  1: {
18
19
  items: { a: 1, b: 2 },
19
20
  nextPage: 2,
@@ -0,0 +1,15 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import { ObjectTree } from "../../src/internal.js";
4
+ import parent from "../../src/operations/parent.js";
5
+
6
+ describe("parent", () => {
7
+ test("returns a tree's parent", async () => {
8
+ const tree = new ObjectTree({
9
+ sub: new ObjectTree({}),
10
+ });
11
+ const sub = await tree.get("sub");
12
+ const result = await parent(sub);
13
+ assert.equal(result, tree);
14
+ });
15
+ });
@@ -0,0 +1,40 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import DeepObjectTree from "../../src/drivers/DeepObjectTree.js";
4
+ import ObjectTree from "../../src/drivers/ObjectTree.js";
5
+ import paths from "../../src/operations/paths.js";
6
+
7
+ describe("paths", () => {
8
+ test("returns an array of paths to the values in the tree", async () => {
9
+ const tree = new DeepObjectTree({
10
+ a: 1,
11
+ b: 2,
12
+ c: {
13
+ d: 3,
14
+ e: 4,
15
+ },
16
+ });
17
+ assert.deepEqual(await paths(tree), ["a", "b", "c/d", "c/e"]);
18
+ });
19
+
20
+ test("can focus just on keys with trailing slashes", async () => {
21
+ const tree = new ObjectTree({
22
+ a: 1,
23
+ b: 2,
24
+ // This is a shallow ObjectTree, so `c` won't have a trailing slash
25
+ c: {
26
+ d: 3,
27
+ },
28
+ // Explicitly include a trailing slash to signal a subtree
29
+ "d/": new ObjectTree({
30
+ e: 4,
31
+ }),
32
+ });
33
+ assert.deepEqual(await paths(tree, { assumeSlashes: true }), [
34
+ "a",
35
+ "b",
36
+ "c",
37
+ "d/e",
38
+ ]);
39
+ });
40
+ });
@@ -0,0 +1,69 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import ObjectTree from "../../src/drivers/ObjectTree.js";
4
+ import plain from "../../src/operations/plain.js";
5
+
6
+ describe("plain", () => {
7
+ test("produces a plain object version of a tree", async () => {
8
+ const tree = new ObjectTree({
9
+ a: 1,
10
+ // Slashes should be normalized
11
+ "sub1/": {
12
+ b: 2,
13
+ },
14
+ sub2: {
15
+ c: 3,
16
+ },
17
+ });
18
+ assert.deepEqual(await plain(tree), {
19
+ a: 1,
20
+ sub1: {
21
+ b: 2,
22
+ },
23
+ sub2: {
24
+ c: 3,
25
+ },
26
+ });
27
+ });
28
+
29
+ test("produces an array for an array-like tree", async () => {
30
+ const original = ["a", "b", "c"];
31
+ const tree = new ObjectTree(original);
32
+ assert.deepEqual(await plain(tree), original);
33
+ });
34
+
35
+ test("leaves an array-like tree as an object if keys aren't consecutive", async () => {
36
+ const original = {
37
+ 0: "a",
38
+ 1: "b",
39
+ // missing
40
+ 3: "c",
41
+ };
42
+ const tree = new ObjectTree(original);
43
+ assert.deepEqual(await plain(tree), original);
44
+ });
45
+
46
+ test("returns empty array or object for ObjectTree as necessary", async () => {
47
+ const tree = new ObjectTree({});
48
+ assert.deepEqual(await plain(tree), {});
49
+ const arrayTree = new ObjectTree([]);
50
+ assert.deepEqual(await plain(arrayTree), []);
51
+ });
52
+
53
+ test("awaits async properties", async () => {
54
+ const object = {
55
+ get name() {
56
+ return Promise.resolve("Alice");
57
+ },
58
+ };
59
+ assert.deepEqual(await plain(object), { name: "Alice" });
60
+ });
61
+
62
+ test("coerces TypedArray values to strings", async () => {
63
+ const tree = new ObjectTree({
64
+ a: new TextEncoder().encode("Hello, world."),
65
+ });
66
+ const result = await plain(tree);
67
+ assert.equal(result.a, "Hello, world.");
68
+ });
69
+ });
@@ -10,7 +10,7 @@ describe("reverse", () => {
10
10
  b: "B",
11
11
  c: "C",
12
12
  };
13
- const reversed = reverse.call(null, tree);
13
+ const reversed = await reverse.call(null, tree);
14
14
  // @ts-ignore
15
15
  assert.deepEqual(Array.from(await reversed.keys()), ["c", "b", "a"]);
16
16
  // @ts-ignore
@@ -13,7 +13,7 @@ describe("scope", () => {
13
13
  a: 3,
14
14
  });
15
15
  inner.parent = outer;
16
- const innerScope = scope(inner);
16
+ const innerScope = await scope(inner);
17
17
  assert.deepEqual([...(await innerScope.keys())], ["a", "b"]);
18
18
  // Inner tree has precedence
19
19
  assert.equal(await innerScope.get("a"), 3);
@@ -0,0 +1,53 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import { DeepObjectTree, ObjectTree, Tree } from "../../src/internal.js";
4
+ import setDeep from "../../src/operations/setDeep.js";
5
+
6
+ describe("tree/setDeep", () => {
7
+ test("can apply updates with a single argument to set", async () => {
8
+ const tree = new DeepObjectTree({
9
+ a: 1,
10
+ b: 2,
11
+ more: {
12
+ d: 3,
13
+ },
14
+ });
15
+
16
+ // Apply changes.
17
+ await setDeep.call(
18
+ null,
19
+ tree,
20
+ new DeepObjectTree({
21
+ a: 4, // Overwrite existing value
22
+ b: undefined, // Delete
23
+ c: 5, // Add
24
+ more: {
25
+ // Should leave existing `more` keys alone.
26
+ e: 6, // Add
27
+ },
28
+ // Add new subtree
29
+ extra: {
30
+ f: 7,
31
+ },
32
+ })
33
+ );
34
+
35
+ assert.deepEqual(await Tree.plain(tree), {
36
+ a: 4,
37
+ c: 5,
38
+ more: {
39
+ d: 3,
40
+ e: 6,
41
+ },
42
+ extra: {
43
+ f: 7,
44
+ },
45
+ });
46
+ });
47
+
48
+ test("can apply updates to an array", async () => {
49
+ const tree = new ObjectTree(["a", "b", "c"]);
50
+ await setDeep.call(null, tree, ["d", "e"]);
51
+ assert.deepEqual(await Tree.plain(tree), ["d", "e", "c"]);
52
+ });
53
+ });
@@ -0,0 +1,18 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import shuffle from "../../src/operations/shuffle.js";
4
+
5
+ describe("shuffle", () => {
6
+ test("shuffles the keys of a tree", async () => {
7
+ const obj = {
8
+ a: 1,
9
+ b: 2,
10
+ c: 3,
11
+ d: 4,
12
+ e: 5,
13
+ };
14
+ const result = await shuffle(obj);
15
+ const keys = Array.from(await result.keys());
16
+ assert.deepEqual(keys.sort(), Object.keys(obj).sort());
17
+ });
18
+ });
@@ -10,7 +10,7 @@ describe("sort", () => {
10
10
  file1: null,
11
11
  file9: null,
12
12
  });
13
- const sorted = sort(tree);
13
+ const sorted = await sort(tree);
14
14
  assert.deepEqual(Array.from(await sorted.keys()), [
15
15
  "file1",
16
16
  "file10",
@@ -26,7 +26,7 @@ describe("sort", () => {
26
26
  });
27
27
  // Reverse order
28
28
  const compare = (a, b) => (a > b ? -1 : a < b ? 1 : 0);
29
- const sorted = sort(tree, { compare });
29
+ const sorted = await sort(tree, { compare });
30
30
  assert.deepEqual(Array.from(await sorted.keys()), ["c", "b", "a"]);
31
31
  });
32
32
 
@@ -37,7 +37,7 @@ describe("sort", () => {
37
37
  Carol: { age: 42 },
38
38
  };
39
39
  const sorted = await sort(tree, {
40
- sortKey: async (key, tree) => Tree.traverse(tree, key, "age"),
40
+ sortKey: async (value, key, tree) => value.age,
41
41
  });
42
42
  assert.deepEqual(Array.from(await sorted.keys()), [
43
43
  "Bob",
@@ -0,0 +1,16 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import ObjectTree from "../../src/drivers/ObjectTree.js";
4
+ import toFunction from "../../src/operations/toFunction.js";
5
+
6
+ describe("toFunction", () => {
7
+ test("returns a function that invokes a tree's get() method", async () => {
8
+ const tree = new ObjectTree({
9
+ a: 1,
10
+ b: 2,
11
+ });
12
+ const fn = await toFunction(tree);
13
+ assert.equal(await fn("a"), 1);
14
+ assert.equal(await fn("b"), 2);
15
+ });
16
+ });
@@ -0,0 +1,43 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import MapTree from "../../src/drivers/MapTree.js";
4
+ import ObjectTree from "../../src/drivers/ObjectTree.js";
5
+ import traverse from "../../src/operations/traverse.js";
6
+
7
+ describe("traverse", () => {
8
+ test("traverses a path of keys", async () => {
9
+ const tree = new ObjectTree({
10
+ a1: 1,
11
+ a2: {
12
+ b1: 2,
13
+ b2: {
14
+ c1: 3,
15
+ c2: 4,
16
+ },
17
+ },
18
+ });
19
+ assert.equal(await traverse(tree), tree);
20
+ assert.equal(await traverse(tree, "a1"), 1);
21
+ assert.equal(await traverse(tree, "a2", "b2", "c2"), 4);
22
+ assert.equal(await traverse(tree, "a2", "doesntexist", "c2"), undefined);
23
+ });
24
+
25
+ test("traverses a function with fixed number of arguments", async () => {
26
+ const tree = (a, b) => ({
27
+ c: "Result",
28
+ });
29
+ assert.equal(await traverse(tree, "a", "b", "c"), "Result");
30
+ });
31
+
32
+ test("traverses from one tree into another", async () => {
33
+ const tree = new ObjectTree({
34
+ a: {
35
+ b: new MapTree([
36
+ ["c", "Hello"],
37
+ ["d", "Goodbye"],
38
+ ]),
39
+ },
40
+ });
41
+ assert.equal(await traverse(tree, "a", "b", "c"), "Hello");
42
+ });
43
+ });
@@ -0,0 +1,16 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import traversePath from "../../src/operations/traversePath.js";
4
+
5
+ describe("traversePath", () => {
6
+ test("traversePath() traverses a slash-separated path", async () => {
7
+ const tree = {
8
+ a: {
9
+ b: {
10
+ c: "Hello",
11
+ },
12
+ },
13
+ };
14
+ assert.equal(await traversePath(tree, "a/b/c"), "Hello");
15
+ });
16
+ });