@weborigami/async-tree 0.5.4 → 0.5.6

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 (153) hide show
  1. package/index.ts +16 -6
  2. package/package.json +2 -2
  3. package/shared.js +20 -29
  4. package/src/Tree.js +59 -513
  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 +3 -3
  12. package/src/drivers/MapTree.js +6 -6
  13. package/src/drivers/ObjectTree.js +6 -8
  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/deepMap.js +25 -0
  25. package/src/operations/deepMerge.js +11 -25
  26. package/src/operations/deepReverse.js +6 -7
  27. package/src/operations/deepTake.js +6 -7
  28. package/src/operations/deepText.js +4 -4
  29. package/src/operations/deepValuesIterator.js +8 -6
  30. package/src/operations/delete.js +20 -0
  31. package/src/operations/entries.js +16 -0
  32. package/src/operations/extensionKeyFunctions.js +1 -1
  33. package/src/operations/filter.js +7 -8
  34. package/src/operations/first.js +18 -0
  35. package/src/operations/forEach.js +20 -0
  36. package/src/operations/from.js +77 -0
  37. package/src/operations/globKeys.js +8 -8
  38. package/src/operations/group.js +3 -46
  39. package/src/operations/groupBy.js +51 -0
  40. package/src/operations/has.js +16 -0
  41. package/src/operations/indent.js +4 -2
  42. package/src/operations/inners.js +29 -0
  43. package/src/operations/invokeFunctions.js +5 -4
  44. package/src/operations/isAsyncMutableTree.js +15 -0
  45. package/src/operations/isAsyncTree.js +21 -0
  46. package/src/operations/isTraversable.js +15 -0
  47. package/src/operations/isTreelike.js +33 -0
  48. package/src/operations/json.js +4 -3
  49. package/src/operations/keys.js +14 -0
  50. package/src/operations/length.js +15 -0
  51. package/src/operations/map.js +156 -95
  52. package/src/operations/mapExtension.js +78 -0
  53. package/src/operations/mapReduce.js +44 -0
  54. package/src/operations/mask.js +18 -16
  55. package/src/operations/match.js +74 -0
  56. package/src/operations/merge.js +22 -20
  57. package/src/operations/paginate.js +3 -5
  58. package/src/operations/parent.js +13 -0
  59. package/src/operations/paths.js +51 -0
  60. package/src/operations/plain.js +34 -0
  61. package/src/operations/regExpKeys.js +4 -5
  62. package/src/operations/reverse.js +4 -6
  63. package/src/operations/root.js +17 -0
  64. package/src/operations/scope.js +4 -6
  65. package/src/operations/shuffle.js +46 -0
  66. package/src/operations/sort.js +19 -12
  67. package/src/operations/take.js +3 -5
  68. package/src/operations/text.js +3 -3
  69. package/src/operations/toFunction.js +14 -0
  70. package/src/operations/traverse.js +24 -0
  71. package/src/operations/traverseOrThrow.js +59 -0
  72. package/src/operations/traversePath.js +16 -0
  73. package/src/operations/values.js +15 -0
  74. package/src/operations/withKeys.js +33 -0
  75. package/src/utilities/TypedArray.js +2 -0
  76. package/src/utilities/box.js +20 -0
  77. package/src/utilities/castArraylike.js +38 -0
  78. package/src/utilities/getParent.js +33 -0
  79. package/src/utilities/getRealmObjectPrototype.js +19 -0
  80. package/src/utilities/getTreeArgument.js +43 -0
  81. package/src/utilities/isPacked.js +20 -0
  82. package/src/utilities/isPlainObject.js +29 -0
  83. package/src/utilities/isPrimitive.js +13 -0
  84. package/src/utilities/isStringlike.js +25 -0
  85. package/src/utilities/isUnpackable.js +13 -0
  86. package/src/utilities/keysFromPath.js +34 -0
  87. package/src/utilities/naturalOrder.js +9 -0
  88. package/src/utilities/pathFromKeys.js +18 -0
  89. package/src/utilities/setParent.js +38 -0
  90. package/src/utilities/toFunction.js +40 -0
  91. package/src/utilities/toPlainValue.js +95 -0
  92. package/src/utilities/toString.js +37 -0
  93. package/test/drivers/ExplorableSiteTree.test.js +1 -1
  94. package/test/drivers/FileTree.test.js +1 -1
  95. package/test/drivers/calendarTree.test.js +1 -1
  96. package/test/jsonKeys.test.js +1 -1
  97. package/test/operations/assign.test.js +54 -0
  98. package/test/operations/cache.test.js +1 -1
  99. package/test/operations/cachedKeyFunctions.test.js +16 -16
  100. package/test/operations/clear.test.js +34 -0
  101. package/test/operations/deepMerge.test.js +2 -6
  102. package/test/operations/deepReverse.test.js +1 -1
  103. package/test/operations/delete.test.js +20 -0
  104. package/test/operations/entries.test.js +18 -0
  105. package/test/operations/extensionKeyFunctions.test.js +10 -10
  106. package/test/operations/first.test.js +15 -0
  107. package/test/operations/fixtures/README.md +1 -0
  108. package/test/operations/forEach.test.js +22 -0
  109. package/test/operations/from.test.js +67 -0
  110. package/test/operations/globKeys.test.js +3 -3
  111. package/test/operations/{group.test.js → groupBy.test.js} +4 -4
  112. package/test/operations/has.test.js +15 -0
  113. package/test/operations/inners.test.js +30 -0
  114. package/test/operations/invokeFunctions.test.js +1 -1
  115. package/test/operations/isAsyncMutableTree.test.js +17 -0
  116. package/test/operations/isAsyncTree.test.js +26 -0
  117. package/test/operations/isTreelike.test.js +13 -0
  118. package/test/operations/keys.test.js +15 -0
  119. package/test/operations/length.test.js +15 -0
  120. package/test/operations/map.test.js +39 -70
  121. package/test/operations/mapExtension.test.js +53 -0
  122. package/test/operations/mapReduce.test.js +23 -0
  123. package/test/operations/mask.test.js +1 -1
  124. package/test/operations/match.test.js +33 -0
  125. package/test/operations/merge.test.js +23 -9
  126. package/test/operations/paginate.test.js +1 -1
  127. package/test/operations/parent.test.js +15 -0
  128. package/test/operations/paths.test.js +40 -0
  129. package/test/operations/plain.test.js +69 -0
  130. package/test/operations/reverse.test.js +1 -1
  131. package/test/operations/scope.test.js +1 -1
  132. package/test/operations/shuffle.test.js +18 -0
  133. package/test/operations/sort.test.js +3 -3
  134. package/test/operations/toFunction.test.js +16 -0
  135. package/test/operations/traverse.test.js +43 -0
  136. package/test/operations/traversePath.test.js +16 -0
  137. package/test/operations/values.test.js +18 -0
  138. package/test/operations/withKeys.test.js +21 -0
  139. package/test/utilities/box.test.js +26 -0
  140. package/test/utilities/getRealmObjectPrototype.test.js +11 -0
  141. package/test/utilities/isPlainObject.test.js +13 -0
  142. package/test/utilities/keysFromPath.test.js +14 -0
  143. package/test/utilities/naturalOrder.test.js +11 -0
  144. package/test/utilities/pathFromKeys.test.js +12 -0
  145. package/test/utilities/setParent.test.js +34 -0
  146. package/test/utilities/toFunction.test.js +34 -0
  147. package/test/utilities/toPlainValue.test.js +27 -0
  148. package/test/utilities/toString.test.js +22 -0
  149. package/src/Tree.d.ts +0 -24
  150. package/src/utilities.d.ts +0 -21
  151. package/src/utilities.js +0 -443
  152. package/test/Tree.test.js +0 -407
  153. package/test/utilities.test.js +0 -141
@@ -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
  });
@@ -12,7 +12,7 @@ describe("paginate", () => {
12
12
  d: 4,
13
13
  e: 5,
14
14
  };
15
- const paginated = await paginate.call(null, treelike, 2);
15
+ const paginated = await paginate(treelike, 2);
16
16
  const plain = await Tree.plain(paginated);
17
17
  assert.deepEqual(await plain, {
18
18
  1: {
@@ -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(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,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
+ });
@@ -0,0 +1,18 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import values from "../../src/operations/values.js";
4
+
5
+ describe("values", () => {
6
+ test("returns the tree's values as an array", async () => {
7
+ const fixture = {
8
+ "Alice.md": "Hello, **Alice**.",
9
+ "Bob.md": "Hello, **Bob**.",
10
+ "Carol.md": "Hello, **Carol**.",
11
+ };
12
+ assert.deepEqual(await values(fixture), [
13
+ "Hello, **Alice**.",
14
+ "Hello, **Bob**.",
15
+ "Hello, **Carol**.",
16
+ ]);
17
+ });
18
+ });
@@ -0,0 +1,21 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import { Tree } from "../../src/internal.js";
4
+ import withKeys from "../../src/operations/withKeys.js";
5
+
6
+ describe("withKeys", () => {
7
+ test("applies the indicated keys", async () => {
8
+ const result = await withKeys(
9
+ {
10
+ a: 1,
11
+ b: 2,
12
+ c: 3,
13
+ },
14
+ ["a", "c"]
15
+ );
16
+ assert.deepEqual(await Tree.plain(result), {
17
+ a: 1,
18
+ c: 3,
19
+ });
20
+ });
21
+ });
@@ -0,0 +1,26 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import box from "../../src/utilities/box.js";
4
+
5
+ describe("box", () => {
6
+ test("returns a boxed value", () => {
7
+ const string = "string";
8
+ const stringObject = box(string);
9
+ assert(stringObject instanceof String);
10
+ assert.equal(stringObject, string);
11
+
12
+ const number = 1;
13
+ const numberObject = box(number);
14
+ assert(numberObject instanceof Number);
15
+ assert.equal(numberObject, number);
16
+
17
+ const boolean = true;
18
+ const booleanObject = box(boolean);
19
+ assert(booleanObject instanceof Boolean);
20
+ assert.equal(booleanObject, boolean);
21
+
22
+ const object = {};
23
+ const boxedObject = box(object);
24
+ assert.equal(boxedObject, object);
25
+ });
26
+ });
@@ -0,0 +1,11 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import getRealmObjectPrototype from "../../src/utilities/getRealmObjectPrototype.js";
4
+
5
+ describe("getRealmObjectPrototype", () => {
6
+ test("returns the object's root prototype", () => {
7
+ const object = {};
8
+ const proto = getRealmObjectPrototype(object);
9
+ assert.equal(proto, Object.prototype);
10
+ });
11
+ });
@@ -0,0 +1,13 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import isPlainObject from "../../src/utilities/isPlainObject.js";
4
+
5
+ describe("isPlainObject", () => {
6
+ test("returns true if the object is a plain object", () => {
7
+ assert.equal(isPlainObject({}), true);
8
+ assert.equal(isPlainObject(new Object()), true);
9
+ assert.equal(isPlainObject(Object.create(null)), true);
10
+ class Foo {}
11
+ assert.equal(isPlainObject(new Foo()), false);
12
+ });
13
+ });
@@ -0,0 +1,14 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import keysFromPath from "../../src/utilities/keysFromPath.js";
4
+
5
+ describe("keysFromPath", () => {
6
+ test("returns the keys from a slash-separated path", () => {
7
+ assert.deepEqual(keysFromPath(""), []);
8
+ assert.deepEqual(keysFromPath("/"), []);
9
+ assert.deepEqual(keysFromPath("a/b/c"), ["a/", "b/", "c"]);
10
+ assert.deepEqual(keysFromPath("a/b/c/"), ["a/", "b/", "c/"]);
11
+ assert.deepEqual(keysFromPath("/foo/"), ["foo/"]);
12
+ assert.deepEqual(keysFromPath("a///b"), ["a/", "b"]);
13
+ });
14
+ });
@@ -0,0 +1,11 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import naturalOrder from "../../src/utilities/naturalOrder.js";
4
+
5
+ describe("naturalOrder", () => {
6
+ test("compares strings in natural order", () => {
7
+ const strings = ["file10", "file1", "file9"];
8
+ strings.sort(naturalOrder);
9
+ assert.deepEqual(strings, ["file1", "file9", "file10"]);
10
+ });
11
+ });
@@ -0,0 +1,12 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import pathFromKeys from "../../src/utilities/pathFromKeys.js";
4
+
5
+ describe("pathFromKeys", () => {
6
+ test("returns a slash-separated path from keys", () => {
7
+ assert.equal(pathFromKeys([]), "");
8
+ assert.equal(pathFromKeys(["a", "b", "c"]), "a/b/c");
9
+ assert.equal(pathFromKeys(["a/", "b/", "c"]), "a/b/c");
10
+ assert.equal(pathFromKeys(["a/", "b/", "c/"]), "a/b/c/");
11
+ });
12
+ });
@@ -0,0 +1,34 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import ObjectTree from "../../src/drivers/ObjectTree.js";
4
+ import * as symbols from "../../src/symbols.js";
5
+ import setParent from "../../src/utilities/setParent.js";
6
+
7
+ describe("setParent", () => {
8
+ test("sets a child's parent", () => {
9
+ const parent = new ObjectTree({});
10
+
11
+ // Set [symbols.parent] on a plain object.
12
+ const object = {};
13
+ setParent(object, parent);
14
+ assert.equal(object[symbols.parent], parent);
15
+
16
+ // Leave [symbols.parent] alone if it's already set.
17
+ const childWithParent = {
18
+ [symbols.parent]: "parent",
19
+ };
20
+ setParent(childWithParent, parent);
21
+ assert.equal(childWithParent[symbols.parent], "parent");
22
+
23
+ // Set `parent` on a tree.
24
+ const tree = new ObjectTree({});
25
+ setParent(tree, parent);
26
+ assert.equal(tree.parent, parent);
27
+
28
+ // Leave `parent` alone if it's already set.
29
+ const treeWithParent = new ObjectTree({});
30
+ treeWithParent.parent = "parent";
31
+ setParent(treeWithParent, parent);
32
+ assert.equal(treeWithParent.parent, "parent");
33
+ });
34
+ });
@@ -0,0 +1,34 @@
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/utilities/toFunction.js";
5
+
6
+ describe("toFunction", () => {
7
+ test("returns a plain function as is", () => {
8
+ const fn = () => {};
9
+ assert.equal(toFunction(fn), fn);
10
+ });
11
+
12
+ test("returns a tree's getter as a function", async () => {
13
+ const tree = new ObjectTree({
14
+ a: 1,
15
+ });
16
+ const fn = toFunction(tree);
17
+ // @ts-ignore
18
+ assert.equal(await fn("a"), 1);
19
+ });
20
+
21
+ test("can use a packed object's `unpack` as a function", async () => {
22
+ const obj = new String();
23
+ /** @type {any} */ (obj).unpack = () => () => "result";
24
+ const fn = toFunction(obj);
25
+ // @ts-ignore
26
+ assert.equal(await fn(), "result");
27
+ });
28
+
29
+ test("returns null for something that's not a function", () => {
30
+ // @ts-ignore
31
+ const result = toFunction("this is not a function");
32
+ assert.equal(result, null);
33
+ });
34
+ });
@@ -0,0 +1,27 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import toPlainValue from "../../src/utilities/toPlainValue.js";
4
+
5
+ describe("toPlainValue", () => {
6
+ test("returns the plainest representation of an object", async () => {
7
+ class User {
8
+ constructor(name) {
9
+ this.name = name;
10
+ }
11
+ }
12
+
13
+ assert.equal(await toPlainValue(1), 1);
14
+ assert.equal(await toPlainValue("string"), "string");
15
+ assert.deepEqual(await toPlainValue({ a: 1 }), { a: 1 });
16
+ assert.equal(
17
+ await toPlainValue(new TextEncoder().encode("bytes")),
18
+ "bytes"
19
+ );
20
+ // ArrayBuffer with non-printable characters should be returned as base64
21
+ assert.equal(await toPlainValue(new Uint8Array([1, 2, 3]).buffer), "AQID");
22
+ assert.equal(await toPlainValue(async () => "result"), "result");
23
+ assert.deepEqual(await toPlainValue(new User("Alice")), {
24
+ name: "Alice",
25
+ });
26
+ });
27
+ });
@@ -0,0 +1,22 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import toString from "../../src/utilities/toString.js";
4
+
5
+ describe("toString", () => {
6
+ test("returns the value of an object's `toString` method", () => {
7
+ const object = {
8
+ toString: () => "text",
9
+ };
10
+ assert.equal(toString(object), "text");
11
+ });
12
+
13
+ test("returns null for an object with no useful `toString`", () => {
14
+ const object = {};
15
+ assert.equal(toString(object), null);
16
+ });
17
+
18
+ test("decodes an ArrayBuffer as UTF-8", () => {
19
+ const arrayBuffer = new TextEncoder().encode("text").buffer;
20
+ assert.equal(toString(arrayBuffer), "text");
21
+ });
22
+ });
package/src/Tree.d.ts DELETED
@@ -1,24 +0,0 @@
1
- import type { AsyncMutableTree, AsyncTree } from "@weborigami/types";
2
- import { PlainObject, ReduceFn, Treelike, TreeMapOptions, ValueKeyFn } from "../index.ts";
3
-
4
- export function assign(target: Treelike, source: Treelike): Promise<AsyncTree>;
5
- export function clear(AsyncTree: AsyncMutableTree): Promise<void>;
6
- export function entries(AsyncTree: AsyncTree): Promise<IterableIterator<any>>;
7
- export function forEach(AsyncTree: AsyncTree, callbackfn: (value: any, key: any) => Promise<void>): Promise<void>;
8
- export function from(obj: any, options?: { deep?: boolean, parent?: AsyncTree|null }): AsyncTree;
9
- export function has(AsyncTree: AsyncTree, key: any): Promise<boolean>;
10
- export function isAsyncMutableTree(obj: any): obj is AsyncMutableTree;
11
- export function isAsyncTree(obj: any): obj is AsyncTree;
12
- export function isTraversable(obj: any): boolean;
13
- export function isTreelike(obj: any): obj is Treelike;
14
- export function map(tree: Treelike, options: TreeMapOptions|ValueKeyFn): AsyncTree;
15
- export function mapReduce(tree: Treelike, mapFn: ValueKeyFn | null, reduceFn: ReduceFn): Promise<any>;
16
- export function paths(tree: Treelike, options?: { assumeSlashes?: boolean, base?: string }): string[];
17
- export function plain(tree: Treelike): Promise<PlainObject>;
18
- export function root(tree: Treelike): AsyncTree;
19
- export function remove(AsyncTree: AsyncMutableTree, key: any): Promise<boolean>;
20
- export function toFunction(tree: Treelike): Function;
21
- export function traverse(tree: Treelike, ...keys: any[]): Promise<any>;
22
- export function traverseOrThrow(tree: Treelike, ...keys: any[]): Promise<any>;
23
- export function traversePath(tree: Treelike, path: string): Promise<any>;
24
- export function values(tree: Treelike): Promise<IterableIterator<any>>;