@weborigami/async-tree 0.0.39 → 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main.js CHANGED
@@ -6,6 +6,7 @@ export { default as FunctionTree } from "./src/FunctionTree.js";
6
6
  export { default as MapTree } from "./src/MapTree.js";
7
7
  export { default as ObjectTree } from "./src/ObjectTree.js";
8
8
  // Skip BrowserFileTree.js, which is browser-only.
9
+ export { default as DeepObjectTree } from "./src/DeepObjectTree.js";
9
10
  export { default as SetTree } from "./src/SetTree.js";
10
11
  export { default as SiteTree } from "./src/SiteTree.js";
11
12
  export * as Tree from "./src/Tree.js";
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "Asynchronous tree drivers based on standard JavaScript classes",
5
5
  "type": "module",
6
6
  "main": "./main.js",
7
7
  "browser": "./browser.js",
8
8
  "types": "./index.ts",
9
9
  "devDependencies": {
10
- "@types/node": "20.11.3",
10
+ "@types/node": "20.11.7",
11
11
  "typescript": "5.3.3"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/types": "*"
14
+ "@weborigami/types": "0.0.40"
15
15
  },
16
16
  "scripts": {
17
- "test": "node --test",
17
+ "test": "node --test --test-reporter=spec",
18
18
  "typecheck": "tsc"
19
19
  }
20
20
  }
@@ -0,0 +1,27 @@
1
+ import ObjectTree from "./ObjectTree.js";
2
+ import * as Tree from "./Tree.js";
3
+ import { isPlainObject } from "./utilities.js";
4
+
5
+ export default class DeepObjectTree extends ObjectTree {
6
+ async get(key) {
7
+ let value = await super.get(key);
8
+
9
+ const isPlain =
10
+ value instanceof Array ||
11
+ (isPlainObject(value) && !Tree.isAsyncTree(value));
12
+ if (isPlain) {
13
+ value = Reflect.construct(this.constructor, [value]);
14
+ }
15
+
16
+ if (Tree.isAsyncTree(value) && !value.parent) {
17
+ value.parent = this;
18
+ }
19
+
20
+ return value;
21
+ }
22
+
23
+ async isKeyForSubtree(key) {
24
+ const value = this.object[key];
25
+ return isPlainObject(value) || Tree.isAsyncTree(value);
26
+ }
27
+ }
package/src/MapTree.js CHANGED
@@ -24,10 +24,6 @@ export default class MapTree {
24
24
  async get(key) {
25
25
  let value = this.map.get(key);
26
26
 
27
- if (value instanceof Map) {
28
- value = Reflect.construct(this.constructor, [value]);
29
- }
30
-
31
27
  if (Tree.isAsyncTree(value) && !value.parent) {
32
28
  value.parent = this;
33
29
  }
@@ -37,7 +33,7 @@ export default class MapTree {
37
33
 
38
34
  async isKeyForSubtree(key) {
39
35
  const value = this.map.get(key);
40
- return value instanceof Map || Tree.isAsyncTree(value);
36
+ return Tree.isAsyncTree(value);
41
37
  }
42
38
 
43
39
  async keys() {
package/src/ObjectTree.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as Tree from "./Tree.js";
2
- import { getRealmObjectPrototype, isPlainObject } from "./utilities.js";
2
+ import { getRealmObjectPrototype } from "./utilities.js";
3
3
 
4
4
  /**
5
5
  * A tree defined by a plain object or array.
@@ -37,13 +37,6 @@ export default class ObjectTree {
37
37
 
38
38
  let value = this.object[key];
39
39
 
40
- const isPlain =
41
- value instanceof Array ||
42
- (isPlainObject(value) && !Tree.isAsyncTree(value));
43
- if (isPlain) {
44
- value = Reflect.construct(this.constructor, [value]);
45
- }
46
-
47
40
  if (Tree.isAsyncTree(value) && !value.parent) {
48
41
  value.parent = this;
49
42
  }
@@ -53,7 +46,7 @@ export default class ObjectTree {
53
46
 
54
47
  async isKeyForSubtree(key) {
55
48
  const value = this.object[key];
56
- return isPlainObject(value) || Tree.isAsyncTree(value);
49
+ return Tree.isAsyncTree(value);
57
50
  }
58
51
 
59
52
  /**
package/src/SetTree.js CHANGED
@@ -18,10 +18,6 @@ export default class SetTree {
18
18
  async get(key) {
19
19
  let value = this.values[key];
20
20
 
21
- if (value instanceof Set) {
22
- value = Reflect.construct(this.constructor, [value]);
23
- }
24
-
25
21
  if (Tree.isAsyncTree(value) && !value.parent) {
26
22
  value.parent = this;
27
23
  }
@@ -31,7 +27,7 @@ export default class SetTree {
31
27
 
32
28
  async isKeyForSubtree(key) {
33
29
  const value = this.values[key];
34
- return value instanceof Set || Tree.isAsyncTree(value);
30
+ return Tree.isAsyncTree(value);
35
31
  }
36
32
 
37
33
  async keys() {
package/src/Tree.js CHANGED
@@ -111,9 +111,12 @@ export function from(obj) {
111
111
  } else if (obj instanceof Set) {
112
112
  return new SetTree(obj);
113
113
  } else if (obj && typeof obj === "object" && "unpack" in obj) {
114
- // Invoke unpack and convert the result to a tree.
115
- let result = obj.unpack();
116
- return result instanceof Promise ? new DeferredTree(result) : from(result);
114
+ async function AsyncFunction() {} // Sample async function
115
+ return obj.unpack instanceof AsyncFunction.constructor
116
+ ? // Async unpack: return a deferred tree.
117
+ new DeferredTree(obj.unpack)
118
+ : // Synchronous unpack: cast the result of unpack() to a tree.
119
+ from(obj.unpack());
117
120
  } else if (obj && typeof obj === "object") {
118
121
  // An instance of some class.
119
122
  return new ObjectTree(obj);
@@ -26,6 +26,7 @@ export default function createGroupByTransform(groupKeyFn) {
26
26
  // A single value was returned
27
27
  groups = [groups];
28
28
  }
29
+ groups = Tree.from(groups);
29
30
 
30
31
  // Add the value to each group.
31
32
  for (const groupKey of await groups.keys()) {
@@ -0,0 +1,24 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import DeepObjectTree from "../src/DeepObjectTree.js";
4
+ import * as Tree from "../src/Tree.js";
5
+
6
+ describe("DeepObjectTree", () => {
7
+ test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
8
+ const tree = new DeepObjectTree({
9
+ a: 1,
10
+ object: {
11
+ b: 2,
12
+ },
13
+ array: [3],
14
+ });
15
+
16
+ const object = await tree.get("object");
17
+ assert.equal(object instanceof DeepObjectTree, true);
18
+ assert.deepEqual(await Tree.plain(object), { b: 2 });
19
+
20
+ const array = await tree.get("array");
21
+ assert.equal(array instanceof DeepObjectTree, true);
22
+ assert.deepEqual(await Tree.plain(array), [3]);
23
+ });
24
+ });
@@ -76,28 +76,13 @@ describe("ObjectTree", () => {
76
76
  assert.equal(await fixture.get("prop"), "Goodbye");
77
77
  });
78
78
 
79
- test("creates an ObjectTree for subtrees", async () => {
80
- const object = {
81
- a: 1,
82
- more: {
83
- b: 2,
84
- },
85
- };
86
- const fixture = new ObjectTree(object);
87
- const more = await fixture.get("more");
88
- assert.equal(more.constructor, ObjectTree);
89
- const b = await more.get("b");
90
- assert.equal(b, 2);
91
- });
92
-
93
79
  test("sets parent on subtrees", async () => {
94
- const object = {
80
+ const fixture = new ObjectTree({
95
81
  a: 1,
96
- more: {
82
+ more: new ObjectTree({
97
83
  b: 2,
98
- },
99
- };
100
- const fixture = new ObjectTree(object);
84
+ }),
85
+ });
101
86
  const more = await fixture.get("more");
102
87
  assert.equal(more.parent, fixture);
103
88
  });
@@ -105,13 +90,13 @@ describe("ObjectTree", () => {
105
90
  test("isKeyForSubtree() indicates which values are subtrees", async () => {
106
91
  const tree = new ObjectTree({
107
92
  a1: 1,
108
- a2: {
93
+ a2: new ObjectTree({
109
94
  b1: 2,
110
- },
95
+ }),
111
96
  a3: 3,
112
- a4: {
97
+ a4: new ObjectTree({
113
98
  b2: 4,
114
- },
99
+ }),
115
100
  });
116
101
  const keys = Array.from(await tree.keys());
117
102
  const subtrees = await Promise.all(
@@ -120,24 +105,6 @@ describe("ObjectTree", () => {
120
105
  assert.deepEqual(subtrees, [false, true, false, true]);
121
106
  });
122
107
 
123
- test("returns an ObjectTree for value that's a plain sub-object or sub-array", async () => {
124
- const tree = new ObjectTree({
125
- a: 1,
126
- object: {
127
- b: 2,
128
- },
129
- array: [3],
130
- });
131
-
132
- const object = await tree.get("object");
133
- assert.equal(object instanceof ObjectTree, true);
134
- assert.deepEqual(await Tree.plain(object), { b: 2 });
135
-
136
- const array = await tree.get("array");
137
- assert.equal(array instanceof ObjectTree, true);
138
- assert.deepEqual(await Tree.plain(array), [3]);
139
- });
140
-
141
108
  test("returns an async tree value as is", async () => {
142
109
  const subtree = {
143
110
  async get(key) {},
@@ -1,5 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
+ import ObjectTree from "../src/ObjectTree.js";
3
4
  import SetTree from "../src/SetTree.js";
4
5
 
5
6
  describe("SetTree", () => {
@@ -24,7 +25,7 @@ describe("SetTree", () => {
24
25
 
25
26
  test("sets parent on subtrees", async () => {
26
27
  const set = new Set();
27
- set.add(new Set("a"));
28
+ set.add(new ObjectTree({}));
28
29
  const fixture = new SetTree(set);
29
30
  const subtree = await fixture.get(0);
30
31
  assert.equal(subtree.parent, fixture);
package/test/Tree.test.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
+ import { DeepObjectTree } from "../main.js";
3
4
  import MapTree from "../src/MapTree.js";
4
5
  import ObjectTree from "../src/ObjectTree.js";
5
6
  import * as Tree from "../src/Tree.js";
6
7
 
7
8
  describe("Tree", () => {
8
9
  test("assign applies one tree to another", async () => {
9
- const target = new ObjectTree({
10
+ const target = new DeepObjectTree({
10
11
  a: 1,
11
12
  b: 2,
12
13
  more: {
@@ -14,7 +15,7 @@ describe("Tree", () => {
14
15
  },
15
16
  });
16
17
 
17
- const source = {
18
+ const source = new DeepObjectTree({
18
19
  a: 4, // Overwrite existing value
19
20
  b: undefined, // Delete
20
21
  c: 5, // Add
@@ -26,7 +27,7 @@ describe("Tree", () => {
26
27
  extra: {
27
28
  f: 7,
28
29
  },
29
- };
30
+ });
30
31
 
31
32
  // Apply changes.
32
33
  const result = await Tree.assign(target, source);
@@ -165,7 +166,7 @@ describe("Tree", () => {
165
166
  });
166
167
 
167
168
  test("isKeyForSubtree() returns true if the key is for a subtree", async () => {
168
- const tree = new ObjectTree({
169
+ const tree = new DeepObjectTree({
169
170
  a: 1,
170
171
  more: {
171
172
  b: 2,
@@ -176,12 +177,12 @@ describe("Tree", () => {
176
177
  });
177
178
 
178
179
  test("map() maps values", async () => {
179
- const tree = {
180
+ const tree = new DeepObjectTree({
180
181
  a: "Alice",
181
182
  more: {
182
183
  b: "Bob",
183
184
  },
184
- };
185
+ });
185
186
  const mapped = Tree.map(tree, (value) => value.toUpperCase());
186
187
  assert.deepEqual(await Tree.plain(mapped), {
187
188
  a: "ALICE",
@@ -192,14 +193,14 @@ describe("Tree", () => {
192
193
  });
193
194
 
194
195
  test("mapReduce() can map values and reduce them", async () => {
195
- const tree = {
196
+ const tree = new DeepObjectTree({
196
197
  a: 1,
197
198
  b: 2,
198
199
  more: {
199
200
  c: 3,
200
201
  },
201
202
  d: 4,
202
- };
203
+ });
203
204
  const reduced = await Tree.mapReduce(
204
205
  tree,
205
206
  (value) => value,
@@ -1,12 +1,13 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
+ import DeepObjectTree from "../../src/DeepObjectTree.js";
3
4
  import * as Tree from "../../src/Tree.js";
4
5
  import mergeDeep from "../../src/operations/mergeDeep.js";
5
6
 
6
7
  describe("mergeDeep", () => {
7
8
  test("can merge deep", async () => {
8
9
  const fixture = mergeDeep(
9
- Tree.from({
10
+ new DeepObjectTree({
10
11
  a: {
11
12
  b: 1,
12
13
  c: {
@@ -14,7 +15,7 @@ describe("mergeDeep", () => {
14
15
  },
15
16
  },
16
17
  }),
17
- Tree.from({
18
+ new DeepObjectTree({
18
19
  a: {
19
20
  b: 0, // Will be obscured by `b` above
20
21
  c: {
@@ -12,9 +12,7 @@ describe("groupBy transform", () => {
12
12
  { name: "Work Sans", tags: ["Grotesque", "Sans Serif"] },
13
13
  ];
14
14
  const tree = Tree.from(fonts);
15
- const grouped = await groupBy((value, key, tree) => value.get("tags"))(
16
- tree
17
- );
15
+ const grouped = await groupBy((value, key, tree) => value.tags)(tree);
18
16
  assert.deepEqual(await Tree.plain(grouped), {
19
17
  Geometric: [{ name: "Albert Sans", tags: ["Geometric", "Sans Serif"] }],
20
18
  Grotesque: [{ name: "Work Sans", tags: ["Grotesque", "Sans Serif"] }],
@@ -1,5 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
+ import DeepObjectTree from "../../src/DeepObjectTree.js";
3
4
  import FunctionTree from "../../src/FunctionTree.js";
4
5
  import ObjectTree from "../../src/ObjectTree.js";
5
6
  import * as Tree from "../../src/Tree.js";
@@ -125,7 +126,7 @@ describe("map", () => {
125
126
  });
126
127
 
127
128
  test("deep maps values", async () => {
128
- const tree = new ObjectTree({
129
+ const tree = new DeepObjectTree({
129
130
  a: "letter a",
130
131
  more: {
131
132
  b: "letter b",
@@ -145,7 +146,7 @@ describe("map", () => {
145
146
  });
146
147
 
147
148
  test("deep maps leaf keys", async () => {
148
- const tree = new ObjectTree({
149
+ const tree = new DeepObjectTree({
149
150
  a: "letter a",
150
151
  more: {
151
152
  b: "letter b",
@@ -166,7 +167,7 @@ describe("map", () => {
166
167
  });
167
168
 
168
169
  test("deep maps leaf keys and values", async () => {
169
- const tree = new ObjectTree({
170
+ const tree = new DeepObjectTree({
170
171
  a: "letter a",
171
172
  more: {
172
173
  b: "letter b",
@@ -1,12 +1,13 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
+ import DeepObjectTree from "../../src/DeepObjectTree.js";
3
4
  import * as Tree from "../../src/Tree.js";
4
5
  import regExpKeys from "../../src/transforms/regExpKeys.js";
5
6
 
6
7
  describe("regExpKeys", () => {
7
8
  test("matches keys using regular expressions", async () => {
8
9
  const fixture = await regExpKeys(
9
- Tree.from({
10
+ new DeepObjectTree({
10
11
  a: true,
11
12
  "b.*": true,
12
13
  c: {