@weborigami/async-tree 0.5.4 → 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.
- package/index.ts +16 -6
- package/package.json +2 -2
- package/shared.js +20 -30
- package/src/Tree.js +62 -513
- package/src/constants.js +2 -0
- package/src/drivers/BrowserFileTree.js +9 -10
- package/src/drivers/DeepMapTree.js +3 -3
- package/src/drivers/DeepObjectTree.js +4 -5
- package/src/drivers/DeferredTree.js +2 -2
- package/src/drivers/FileTree.js +11 -33
- package/src/drivers/FunctionTree.js +1 -1
- package/src/drivers/MapTree.js +6 -6
- package/src/drivers/ObjectTree.js +4 -3
- package/src/drivers/SetTree.js +1 -1
- package/src/drivers/SiteTree.js +1 -1
- package/src/drivers/constantTree.js +1 -1
- package/src/extension.js +5 -3
- package/src/jsonKeys.js +5 -7
- package/src/operations/addNextPrevious.js +10 -9
- package/src/operations/assign.js +40 -0
- package/src/operations/cache.js +18 -12
- package/src/operations/cachedKeyFunctions.js +15 -4
- package/src/operations/clear.js +20 -0
- package/src/operations/concat.js +17 -0
- package/src/operations/deepMap.js +25 -0
- package/src/operations/deepMerge.js +11 -25
- package/src/operations/deepReverse.js +6 -7
- package/src/operations/deepTake.js +6 -7
- package/src/operations/deepText.js +4 -4
- package/src/operations/deepValuesIterator.js +8 -6
- package/src/operations/defineds.js +32 -0
- package/src/operations/delete.js +20 -0
- package/src/operations/entries.js +16 -0
- package/src/operations/extensionKeyFunctions.js +1 -1
- package/src/operations/filter.js +7 -8
- package/src/operations/first.js +18 -0
- package/src/operations/forEach.js +20 -0
- package/src/operations/from.js +77 -0
- package/src/operations/fromFn.js +26 -0
- package/src/operations/globKeys.js +8 -8
- package/src/operations/group.js +9 -7
- package/src/operations/has.js +16 -0
- package/src/operations/indent.js +4 -2
- package/src/operations/inners.js +29 -0
- package/src/operations/invokeFunctions.js +5 -4
- package/src/operations/isAsyncMutableTree.js +15 -0
- package/src/operations/isAsyncTree.js +21 -0
- package/src/operations/isTraversable.js +15 -0
- package/src/operations/isTreelike.js +33 -0
- package/src/operations/json.js +4 -3
- package/src/operations/keys.js +14 -0
- package/src/operations/length.js +15 -0
- package/src/operations/map.js +151 -95
- package/src/operations/mapExtension.js +27 -0
- package/src/operations/mapReduce.js +44 -0
- package/src/operations/mask.js +18 -16
- package/src/operations/match.js +74 -0
- package/src/operations/merge.js +22 -20
- package/src/operations/paginate.js +3 -5
- package/src/operations/parent.js +13 -0
- package/src/operations/paths.js +51 -0
- package/src/operations/plain.js +34 -0
- package/src/operations/regExpKeys.js +4 -5
- package/src/operations/remove.js +14 -0
- package/src/operations/reverse.js +4 -6
- package/src/operations/root.js +17 -0
- package/src/operations/scope.js +4 -6
- package/src/operations/setDeep.js +50 -0
- package/src/operations/shuffle.js +46 -0
- package/src/operations/sort.js +19 -12
- package/src/operations/take.js +3 -5
- package/src/operations/text.js +3 -3
- package/src/operations/toFunction.js +14 -0
- package/src/operations/traverse.js +25 -0
- package/src/operations/traverseOrThrow.js +64 -0
- package/src/operations/traversePath.js +16 -0
- package/src/operations/values.js +15 -0
- package/src/operations/withKeys.js +33 -0
- package/src/utilities/TypedArray.js +2 -0
- package/src/utilities/box.js +20 -0
- package/src/utilities/castArraylike.js +38 -0
- package/src/utilities/getParent.js +33 -0
- package/src/utilities/getRealmObjectPrototype.js +19 -0
- package/src/utilities/getTreeArgument.js +43 -0
- package/src/utilities/isPacked.js +20 -0
- package/src/utilities/isPlainObject.js +29 -0
- package/src/utilities/isPrimitive.js +13 -0
- package/src/utilities/isStringlike.js +25 -0
- package/src/utilities/isUnpackable.js +13 -0
- package/src/utilities/keysFromPath.js +34 -0
- package/src/utilities/naturalOrder.js +9 -0
- package/src/utilities/pathFromKeys.js +18 -0
- package/src/utilities/setParent.js +38 -0
- package/src/utilities/toFunction.js +41 -0
- package/src/utilities/toPlainValue.js +95 -0
- package/src/utilities/toString.js +37 -0
- package/test/drivers/ExplorableSiteTree.test.js +1 -1
- package/test/drivers/FileTree.test.js +1 -1
- package/test/drivers/calendarTree.test.js +1 -1
- package/test/jsonKeys.test.js +1 -1
- package/test/operations/assign.test.js +54 -0
- package/test/operations/cache.test.js +1 -1
- package/test/operations/cachedKeyFunctions.test.js +16 -16
- package/test/operations/clear.test.js +34 -0
- package/test/operations/deepMap.test.js +29 -0
- package/test/operations/deepMerge.test.js +2 -6
- package/test/operations/deepReverse.test.js +1 -1
- package/test/operations/defineds.test.js +25 -0
- package/test/operations/delete.test.js +20 -0
- package/test/operations/entries.test.js +18 -0
- package/test/operations/extensionKeyFunctions.test.js +10 -10
- package/test/operations/first.test.js +15 -0
- package/test/operations/fixtures/README.md +1 -0
- package/test/operations/forEach.test.js +22 -0
- package/test/operations/from.test.js +67 -0
- package/test/operations/globKeys.test.js +3 -3
- package/test/operations/has.test.js +15 -0
- package/test/operations/inners.test.js +30 -0
- package/test/operations/invokeFunctions.test.js +1 -1
- package/test/operations/isAsyncMutableTree.test.js +17 -0
- package/test/operations/isAsyncTree.test.js +26 -0
- package/test/operations/isTreelike.test.js +13 -0
- package/test/operations/keys.test.js +15 -0
- package/test/operations/length.test.js +15 -0
- package/test/operations/map.test.js +61 -42
- package/test/operations/mapExtension.test.js +0 -0
- package/test/operations/mapReduce.test.js +23 -0
- package/test/operations/mask.test.js +1 -1
- package/test/operations/match.test.js +33 -0
- package/test/operations/merge.test.js +23 -9
- package/test/operations/parent.test.js +15 -0
- package/test/operations/paths.test.js +40 -0
- package/test/operations/plain.test.js +69 -0
- package/test/operations/reverse.test.js +1 -1
- package/test/operations/scope.test.js +1 -1
- package/test/operations/setDeep.test.js +53 -0
- package/test/operations/shuffle.test.js +18 -0
- package/test/operations/sort.test.js +3 -3
- package/test/operations/toFunction.test.js +16 -0
- package/test/operations/traverse.test.js +43 -0
- package/test/operations/traversePath.test.js +16 -0
- package/test/operations/values.test.js +18 -0
- package/test/operations/withKeys.test.js +21 -0
- package/test/utilities/box.test.js +26 -0
- package/test/utilities/getRealmObjectPrototype.test.js +11 -0
- package/test/utilities/isPlainObject.test.js +13 -0
- package/test/utilities/keysFromPath.test.js +14 -0
- package/test/utilities/naturalOrder.test.js +11 -0
- package/test/utilities/pathFromKeys.test.js +12 -0
- package/test/utilities/setParent.test.js +34 -0
- package/test/utilities/toFunction.test.js +34 -0
- package/test/utilities/toPlainValue.test.js +27 -0
- package/test/utilities/toString.test.js +22 -0
- package/src/Tree.d.ts +0 -24
- package/src/utilities.d.ts +0 -21
- package/src/utilities.js +0 -443
- package/test/Tree.test.js +0 -407
- 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
|
});
|
|
@@ -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) =>
|
|
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
|
+
});
|