@weborigami/async-tree 0.0.45 → 0.0.47

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 (49) hide show
  1. package/index.ts +18 -1
  2. package/main.js +4 -5
  3. package/package.json +4 -4
  4. package/src/BrowserFileTree.js +1 -1
  5. package/src/DeepMapTree.js +1 -1
  6. package/src/DeepObjectTree.js +1 -2
  7. package/src/DeferredTree.js +22 -9
  8. package/src/FileTree.js +24 -20
  9. package/src/FunctionTree.js +0 -4
  10. package/src/MapTree.js +1 -1
  11. package/src/ObjectTree.js +1 -1
  12. package/src/SetTree.js +1 -1
  13. package/src/SiteTree.js +1 -6
  14. package/src/Tree.d.ts +2 -1
  15. package/src/Tree.js +56 -45
  16. package/src/internal.js +15 -0
  17. package/src/keysJson.js +1 -1
  18. package/src/operations/cache.js +9 -5
  19. package/src/operations/mergeDeep.js +1 -1
  20. package/src/symbols.js +1 -0
  21. package/src/transforms/{cachedKeyMaps.js → cachedKeyFunctions.js} +10 -10
  22. package/src/transforms/groupBy.js +4 -4
  23. package/src/transforms/{keyMapsForExtensions.js → keyFunctionsForExtensions.js} +4 -4
  24. package/src/transforms/map.js +29 -28
  25. package/src/transforms/regExpKeys.js +3 -2
  26. package/src/transforms/sort.js +4 -1
  27. package/src/transforms/sortBy.js +4 -1
  28. package/src/utilities.d.ts +3 -1
  29. package/src/utilities.js +37 -9
  30. package/test/BrowserFileTree.test.js +1 -1
  31. package/test/DeepObjectTree.test.js +1 -2
  32. package/test/DeferredTree.test.js +1 -2
  33. package/test/FileTree.test.js +1 -1
  34. package/test/FunctionTree.test.js +0 -6
  35. package/test/ObjectTree.test.js +1 -2
  36. package/test/SetTree.test.js +1 -1
  37. package/test/SiteTree.test.js +1 -1
  38. package/test/Tree.test.js +12 -22
  39. package/test/operations/cache.test.js +1 -2
  40. package/test/operations/merge.test.js +1 -1
  41. package/test/operations/mergeDeep.test.js +1 -2
  42. package/test/transforms/cachedKeyMaps.test.js +13 -13
  43. package/test/transforms/groupBy.test.js +1 -1
  44. package/test/transforms/keyMapsForExtensions.test.js +20 -21
  45. package/test/transforms/map.test.js +36 -41
  46. package/test/transforms/regExpKeys.test.js +1 -2
  47. package/test/transforms/sort.test.js +1 -1
  48. package/test/transforms/sortBy.test.js +1 -1
  49. package/test/transforms/sortNatural.test.js +1 -1
package/index.ts CHANGED
@@ -15,6 +15,12 @@ export type HasString = {
15
15
  toString(): string;
16
16
  };
17
17
 
18
+ /**
19
+ * A packed value is one that can be written to a file via fs.writeFile or into
20
+ * an HTTP response via response.write, or readily converted to such a form.
21
+ */
22
+ export type Packed = ArrayBuffer | Buffer | ReadableStream | string | String | TypedArray;
23
+
18
24
  export type PlainObject = {
19
25
  [key: string]: any;
20
26
  };
@@ -35,7 +41,18 @@ export type Treelike =
35
41
  NativeTreelike |
36
42
  Unpackable<NativeTreelike>;
37
43
 
38
- export type TreeTransform = (tree: AsyncTree) => AsyncTree;
44
+ export type TreeTransform = (treelike: Treelike) => AsyncTree;
45
+
46
+ export type TypedArray =
47
+ Float32Array |
48
+ Float64Array |
49
+ Int8Array |
50
+ Int16Array |
51
+ Int32Array |
52
+ Uint8Array |
53
+ Uint8ClampedArray |
54
+ Uint16Array |
55
+ Uint32Array;
39
56
 
40
57
  export type Unpackable<T> = {
41
58
  unpack(): Promise<T>
package/main.js CHANGED
@@ -4,20 +4,19 @@ export { default as DeferredTree } from "./src/DeferredTree.js";
4
4
  export { default as FileTree } from "./src/FileTree.js";
5
5
  export { default as FunctionTree } from "./src/FunctionTree.js";
6
6
  export { default as MapTree } from "./src/MapTree.js";
7
- export { default as ObjectTree } from "./src/ObjectTree.js";
8
7
  // Skip BrowserFileTree.js, which is browser-only.
9
8
  export { default as DeepMapTree } from "./src/DeepMapTree.js";
10
- export { default as DeepObjectTree } from "./src/DeepObjectTree.js";
11
9
  export { default as SetTree } from "./src/SetTree.js";
12
10
  export { default as SiteTree } from "./src/SiteTree.js";
13
- export * as Tree from "./src/Tree.js";
11
+ export { DeepObjectTree, ObjectTree, Tree } from "./src/internal.js";
14
12
  export * as keysJson from "./src/keysJson.js";
15
13
  export { default as cache } from "./src/operations/cache.js";
16
14
  export { default as merge } from "./src/operations/merge.js";
17
15
  export { default as mergeDeep } from "./src/operations/mergeDeep.js";
18
- export { default as cachedKeyMaps } from "./src/transforms/cachedKeyMaps.js";
16
+ export * as symbols from "./src/symbols.js";
17
+ export { default as cachedKeyFunctions } from "./src/transforms/cachedKeyFunctions.js";
19
18
  export { default as groupBy } from "./src/transforms/groupBy.js";
20
- export { default as keyMapsForExtensions } from "./src/transforms/keyMapsForExtensions.js";
19
+ export { default as keyFunctionsForExtensions } from "./src/transforms/keyFunctionsForExtensions.js";
21
20
  export { default as map } from "./src/transforms/map.js";
22
21
  export { default as sort } from "./src/transforms/sort.js";
23
22
  export { default as sortBy } from "./src/transforms/sortBy.js";
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@weborigami/async-tree",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
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.7",
11
- "typescript": "5.3.3"
10
+ "@types/node": "20.11.27",
11
+ "typescript": "5.4.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/types": "0.0.45"
14
+ "@weborigami/types": "0.0.47"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "node --test --test-reporter=spec",
@@ -1,4 +1,4 @@
1
- import * as Tree from "./Tree.js";
1
+ import { Tree } from "./internal.js";
2
2
  import { hiddenFileNames, isStringLike, sortNatural } from "./utilities.js";
3
3
 
4
4
  const TypedArray = Object.getPrototypeOf(Uint8Array);
@@ -1,5 +1,5 @@
1
+ import { Tree } from "./internal.js";
1
2
  import MapTree from "./MapTree.js";
2
- import * as Tree from "./Tree.js";
3
3
 
4
4
  export default class DeepMapTree extends MapTree {
5
5
  async get(key) {
@@ -1,5 +1,4 @@
1
- import ObjectTree from "./ObjectTree.js";
2
- import * as Tree from "./Tree.js";
1
+ import { ObjectTree, Tree } from "./internal.js";
3
2
  import { isPlainObject } from "./utilities.js";
4
3
 
5
4
  export default class DeepObjectTree extends ObjectTree {
@@ -1,4 +1,4 @@
1
- import * as Tree from "./Tree.js";
1
+ import { Tree } from "./internal.js";
2
2
 
3
3
  /**
4
4
  * A tree that is loaded lazily.
@@ -19,7 +19,8 @@ export default class DeferredTree {
19
19
  this.loader = loader;
20
20
  this.treePromise = null;
21
21
  this._tree = null;
22
- this._parent = null;
22
+ this._parentUntilLoaded = null;
23
+ this._scopeUntilLoaded = null;
23
24
  }
24
25
 
25
26
  async get(key) {
@@ -39,14 +40,17 @@ export default class DeferredTree {
39
40
  return tree.keys();
40
41
  }
41
42
 
43
+ // A deferred tree's parent generally comes from the loaded tree. However, if
44
+ // someone tries to get or set the parent before the tree is loaded, we store
45
+ // that parent reference and apply it once the tree is loaded.
42
46
  get parent() {
43
- return this._tree?.parent ?? this._parent;
47
+ return this._tree?.parent ?? this._parentUntilLoaded;
44
48
  }
45
49
  set parent(parent) {
46
50
  if (this._tree && !this._tree.parent) {
47
51
  this._tree.parent = parent;
48
52
  } else {
49
- this._parent = parent;
53
+ this._parentUntilLoaded = parent;
50
54
  }
51
55
  }
52
56
 
@@ -61,9 +65,11 @@ export default class DeferredTree {
61
65
  return /** @type {any} */ (this._tree)?.scope;
62
66
  }
63
67
  set scope(scope) {
64
- // If tree hasn't been loaded yet, setting scope has no effect.
65
- if (this._tree) {
68
+ // As with `parent`, we can defer setting of scope.
69
+ if (this._tree && !(/** @type {any} */ (this._tree).scope)) {
66
70
  /** @type {any} */ (this._tree).scope = scope;
71
+ } else {
72
+ this._scopeUntilLoaded = scope;
67
73
  }
68
74
  }
69
75
 
@@ -75,11 +81,18 @@ export default class DeferredTree {
75
81
  // Use a promise to ensure the treelike is only converted to a tree once.
76
82
  this.treePromise ??= this.loadResult().then((treelike) => {
77
83
  this._tree = Tree.from(treelike);
78
- if (this._parent) {
84
+ if (this._parentUntilLoaded) {
85
+ // Now that the tree has been loaded, we can set its parent.
79
86
  if (!this._tree.parent) {
80
- this._tree.parent = this._parent;
87
+ this._tree.parent = this._parentUntilLoaded;
88
+ }
89
+ this._parentUntilLoaded = null;
90
+ }
91
+ if (this._scopeUntilLoaded) {
92
+ if (!(/** @type {any} */ (this._tree).scope)) {
93
+ /** @type {any} */ (this._tree).scope = this._scopeUntilLoaded;
81
94
  }
82
- this._parent = null;
95
+ this._scopeUntilLoaded = null;
83
96
  }
84
97
  return this._tree;
85
98
  });
package/src/FileTree.js CHANGED
@@ -1,15 +1,14 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
- import * as Tree from "./Tree.js";
4
+ import { Tree } from "./internal.js";
5
5
  import {
6
6
  getRealmObjectPrototype,
7
7
  hiddenFileNames,
8
+ isPacked,
8
9
  sortNatural,
9
10
  } from "./utilities.js";
10
11
 
11
- const TypedArray = Object.getPrototypeOf(Uint8Array);
12
-
13
12
  /**
14
13
  * A file system tree via the Node file system API.
15
14
  *
@@ -128,29 +127,34 @@ export default class FileTree {
128
127
  return this;
129
128
  }
130
129
 
131
- // Treat null value as empty string; will create an empty file.
132
- if (value === null) {
133
- value = "";
134
- }
135
-
136
- if (value instanceof ArrayBuffer) {
137
- // Convert ArrayBuffer to Uint8Array, which Node.js can write directly.
138
- value = new Uint8Array(value);
130
+ if (typeof value === "function") {
131
+ // Invoke function; write out the result.
132
+ value = await value();
139
133
  }
140
134
 
141
- // True if fs.writeFile can directly write the value to a file.
142
- let isWriteable =
143
- value instanceof TypedArray ||
144
- value instanceof DataView ||
145
- (globalThis.ReadableStream && value instanceof ReadableStream);
146
-
147
- if (!isWriteable && isStringLike(value)) {
135
+ let packed = false;
136
+ if (value === null) {
137
+ // Treat null value as empty string; will create an empty file.
138
+ value = "";
139
+ packed = true;
140
+ } else if (isPacked(value)) {
141
+ packed = true;
142
+ } else if (typeof value.pack === "function") {
143
+ // Pack the value for writing.
144
+ value = await value.pack();
145
+ packed = true;
146
+ } else if (isStringLike(value)) {
148
147
  // Value has a meaningful `toString` method, use that.
149
148
  value = String(value);
150
- isWriteable = true;
149
+ packed = true;
151
150
  }
152
151
 
153
- if (isWriteable) {
152
+ if (packed) {
153
+ // Single writeable value.
154
+ if (value instanceof ArrayBuffer) {
155
+ // Convert ArrayBuffer to Uint8Array, which Node.js can write directly.
156
+ value = new Uint8Array(value);
157
+ }
154
158
  // Ensure this directory exists.
155
159
  await fs.mkdir(this.dirname, { recursive: true });
156
160
  // Write out the value as the contents of a file.
@@ -41,8 +41,4 @@ export default class FunctionTree {
41
41
  async keys() {
42
42
  return this.domain;
43
43
  }
44
-
45
- async unpack() {
46
- return this.fn;
47
- }
48
44
  }
package/src/MapTree.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as Tree from "./Tree.js";
1
+ import { Tree } from "./internal.js";
2
2
 
3
3
  /**
4
4
  * A tree backed by a JavaScript `Map` object.
package/src/ObjectTree.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as Tree from "./Tree.js";
1
+ import { Tree } from "./internal.js";
2
2
  import { getRealmObjectPrototype } from "./utilities.js";
3
3
 
4
4
  /**
package/src/SetTree.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as Tree from "./Tree.js";
1
+ import { Tree } from "./internal.js";
2
2
 
3
3
  /**
4
4
  * A tree of Set objects.
package/src/SiteTree.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as Tree from "./Tree.js";
1
+ import { Tree } from "./internal.js";
2
2
  import * as keysJson from "./keysJson.js";
3
3
 
4
4
  /**
@@ -152,11 +152,6 @@ export default class SiteTree {
152
152
  return Reflect.construct(this.constructor, [href]);
153
153
  }
154
154
 
155
- async unpack() {
156
- const response = await fetch(this.href);
157
- return response.ok ? response.arrayBuffer() : undefined;
158
- }
159
-
160
155
  get url() {
161
156
  return new URL(this.href);
162
157
  }
package/src/Tree.d.ts CHANGED
@@ -10,8 +10,9 @@ export function has(AsyncTree: AsyncTree, key: any): Promise<boolean>;
10
10
  export function isAsyncMutableTree(obj: any): obj is AsyncMutableTree;
11
11
  export function isAsyncTree(obj: any): obj is AsyncTree;
12
12
  export function isKeyForSubtree(tree: AsyncTree, obj: any): Promise<boolean>;
13
+ export function isTraversable(obj: any): boolean;
13
14
  export function isTreelike(obj: any): obj is Treelike;
14
- export function map(tree: Treelike, valueMap: ValueKeyFn): AsyncTree;
15
+ export function map(tree: Treelike, valueFn: ValueKeyFn): AsyncTree;
15
16
  export function mapReduce(tree: Treelike, mapFn: ValueKeyFn|null, reduceFn: ReduceFn): Promise<any>;
16
17
  export function plain(tree: Treelike): Promise<PlainObject>;
17
18
  export function remove(AsyncTree: AsyncMutableTree, key: any): Promise<boolean>;
package/src/Tree.js CHANGED
@@ -1,11 +1,16 @@
1
1
  import DeferredTree from "./DeferredTree.js";
2
2
  import FunctionTree from "./FunctionTree.js";
3
3
  import MapTree from "./MapTree.js";
4
- import ObjectTree from "./ObjectTree.js";
5
4
  import SetTree from "./SetTree.js";
5
+ import { DeepObjectTree, ObjectTree } from "./internal.js";
6
6
  import mapTransform from "./transforms/map.js";
7
7
  import * as utilities from "./utilities.js";
8
- import { castArrayLike, isPlainObject } from "./utilities.js";
8
+ import {
9
+ castArrayLike,
10
+ isPacked,
11
+ isPlainObject,
12
+ isUnpackable,
13
+ } from "./utilities.js";
9
14
 
10
15
  /**
11
16
  * Helper functions for working with async trees
@@ -112,7 +117,9 @@ export function from(obj) {
112
117
  return new MapTree(obj);
113
118
  } else if (obj instanceof Set) {
114
119
  return new SetTree(obj);
115
- } else if (obj && typeof obj === "object" && "unpack" in obj) {
120
+ } else if (isPlainObject(obj)) {
121
+ return new DeepObjectTree(obj);
122
+ } else if (isUnpackable(obj)) {
116
123
  async function AsyncFunction() {} // Sample async function
117
124
  return obj.unpack instanceof AsyncFunction.constructor
118
125
  ? // Async unpack: return a deferred tree.
@@ -163,23 +170,52 @@ export function isAsyncMutableTree(object) {
163
170
  return isAsyncTree(object) && typeof object.set === "function";
164
171
  }
165
172
 
173
+ /**
174
+ * Return true if the indicated key produces or is expected to produce an
175
+ * async tree.
176
+ *
177
+ * This defers to the tree's own isKeyForSubtree method. If not found, this
178
+ * gets the value of that key and returns true if the value is an async
179
+ * tree.
180
+ */
181
+ export async function isKeyForSubtree(tree, key) {
182
+ if (tree.isKeyForSubtree) {
183
+ return tree.isKeyForSubtree(key);
184
+ }
185
+ const value = await tree.get(key);
186
+ return isAsyncTree(value);
187
+ }
188
+
189
+ /**
190
+ * Return true if the object can be traversed via the `traverse()` method. The
191
+ * object must be either treelike or a packed object with an `unpack()` method.
192
+ *
193
+ * @param {any} object
194
+ */
195
+ export function isTraversable(object) {
196
+ return (
197
+ isTreelike(object) ||
198
+ (isPacked(object) && /** @type {any} */ (object).unpack instanceof Function)
199
+ );
200
+ }
201
+
166
202
  /**
167
203
  * Returns true if the indicated object can be directly treated as an
168
204
  * asynchronous tree. This includes:
169
205
  *
170
206
  * - An object that implements the AsyncTree interface (including
171
207
  * AsyncTree instances)
172
- * - An object that implements the `unpack()` method
173
208
  * - A function
174
209
  * - An `Array` instance
175
210
  * - A `Map` instance
176
211
  * - A `Set` instance
177
212
  * - A plain object
178
213
  *
179
- * Note: the `from()` method accepts any JavaScript object, but `isTreeable`
214
+ * Note: the `from()` method accepts any JavaScript object, but `isTreelike`
180
215
  * returns `false` for an object that isn't one of the above types.
181
216
  *
182
217
  * @param {any} object
218
+ * @returns {obj is Treelike}
183
219
  */
184
220
  export function isTreelike(object) {
185
221
  return (
@@ -187,36 +223,19 @@ export function isTreelike(object) {
187
223
  object instanceof Function ||
188
224
  object instanceof Array ||
189
225
  object instanceof Set ||
190
- object?.unpack instanceof Function ||
191
226
  isPlainObject(object)
192
227
  );
193
228
  }
194
229
 
195
- /**
196
- * Return true if the indicated key produces or is expected to produce an
197
- * async tree.
198
- *
199
- * This defers to the tree's own isKeyForSubtree method. If not found, this
200
- * gets the value of that key and returns true if the value is an async
201
- * tree.
202
- */
203
- export async function isKeyForSubtree(tree, key) {
204
- if (tree.isKeyForSubtree) {
205
- return tree.isKeyForSubtree(key);
206
- }
207
- const value = await tree.get(key);
208
- return isAsyncTree(value);
209
- }
210
-
211
230
  /**
212
231
  * Return a new tree with deeply-mapped values of the original tree.
213
232
  *
214
233
  * @param {Treelike} treelike
215
- * @param {ValueKeyFn} valueMap
234
+ * @param {ValueKeyFn} valueFn
216
235
  */
217
- export function map(treelike, valueMap) {
236
+ export function map(treelike, valueFn) {
218
237
  const tree = from(treelike);
219
- return mapTransform({ deep: true, valueMap })(tree);
238
+ return mapTransform({ deep: true, value: valueFn })(tree);
220
239
  }
221
240
 
222
241
  /**
@@ -230,10 +249,10 @@ export function map(treelike, valueMap) {
230
249
  * reduceFn, which should consolidate those into a single result.
231
250
  *
232
251
  * @param {Treelike} treelike
233
- * @param {ValueKeyFn|null} valueMap
252
+ * @param {ValueKeyFn|null} valueFn
234
253
  * @param {ReduceFn} reduceFn
235
254
  */
236
- export async function mapReduce(treelike, valueMap, reduceFn) {
255
+ export async function mapReduce(treelike, valueFn, reduceFn) {
237
256
  const tree = from(treelike);
238
257
 
239
258
  // We're going to fire off all the get requests in parallel, as quickly as
@@ -244,9 +263,9 @@ export async function mapReduce(treelike, valueMap, reduceFn) {
244
263
  tree.get(key).then((value) =>
245
264
  // If the value is a subtree, recurse.
246
265
  isAsyncTree(value)
247
- ? mapReduce(value, valueMap, reduceFn)
248
- : valueMap
249
- ? valueMap(value, key, tree)
266
+ ? mapReduce(value, valueFn, reduceFn)
267
+ : valueFn
268
+ ? valueFn(value, key, tree)
250
269
  : value
251
270
  )
252
271
  );
@@ -364,24 +383,16 @@ export async function traverseOrThrow(treelike, ...keys) {
364
383
  throw new TraverseError(message, treelike, keys);
365
384
  }
366
385
 
367
- // Special case: one key left that's an empty string
368
- if (remainingKeys.length === 1 && remainingKeys[0] === "") {
369
- // Unpack the value if it defines an `unpack` function, otherwise return
370
- // the value itself.
371
- return typeof value.unpack === "function" ? await value.unpack() : value;
372
- }
373
-
374
- // If the value is not a function or async tree already, but can be
375
- // unpacked, unpack it.
376
- if (
377
- !(value instanceof Function) &&
378
- !isAsyncTree(value) &&
379
- value.unpack instanceof Function
380
- ) {
386
+ // If the value is packed and can be unpacked, unpack it.
387
+ if (isUnpackable(value)) {
381
388
  value = await value.unpack();
382
389
  }
383
390
 
384
- if (value instanceof Function) {
391
+ // Peek ahead: if there's only one key left and it's an empty string, return
392
+ // the value itself.
393
+ if (remainingKeys.length === 1 && remainingKeys[0] === "") {
394
+ return value;
395
+ } else if (value instanceof Function) {
385
396
  // Value is a function: call it with the remaining keys.
386
397
  const fn = value;
387
398
  // We'll take as many keys as the function's length, but at least one.
@@ -0,0 +1,15 @@
1
+ //
2
+ // This library includes a number of modules with circular dependencies. This
3
+ // module exists to explicitly set the loading order for those modules. To
4
+ // enforce use of this loading order, other modules should only load the modules
5
+ // below via this module.
6
+ //
7
+ // About this pattern:
8
+ // https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
9
+ //
10
+
11
+ export * as Tree from "./Tree.js";
12
+
13
+ export { default as ObjectTree } from "./ObjectTree.js";
14
+
15
+ export { default as DeepObjectTree } from "./DeepObjectTree.js";
package/src/keysJson.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as Tree from "./Tree.js";
1
+ import { Tree } from "./internal.js";
2
2
 
3
3
  /**
4
4
  * The .keys.json file format lets a site expose the keys of a node in the site
@@ -1,4 +1,4 @@
1
- import { ObjectTree, Tree } from "@weborigami/async-tree";
1
+ import { DeepObjectTree, Tree } from "../internal.js";
2
2
 
3
3
  /**
4
4
  * Caches values from a source tree in a second cache tree. If no second tree is
@@ -9,15 +9,19 @@ import { ObjectTree, Tree } from "@weborigami/async-tree";
9
9
  *
10
10
  * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
11
11
  * @typedef {import("@weborigami/types").AsyncMutableTree} AsyncMutableTree
12
+ * @typedef {import("../../index.ts").Treelike} Treelike
12
13
  *
13
- * @param {AsyncTree} source
14
+ * @param {Treelike} sourceTreelike
14
15
  * @param {AsyncMutableTree} [cacheTree]
15
- * @param {AsyncTree} [filter]
16
+ * @param {Treelike} [filterTreelike]
16
17
  * @returns {AsyncTree & { description: string }}
17
18
  */
18
- export default function treeCache(source, cacheTree, filter) {
19
+ export default function treeCache(sourceTreelike, cacheTree, filterTreelike) {
20
+ const source = Tree.from(sourceTreelike);
21
+ const filter = filterTreelike ? Tree.from(filterTreelike) : undefined;
22
+
19
23
  /** @type {AsyncMutableTree} */
20
- const cache = cacheTree ?? new ObjectTree({});
24
+ const cache = cacheTree ?? new DeepObjectTree({});
21
25
  return {
22
26
  description: "cache",
23
27
 
@@ -1,4 +1,4 @@
1
- import * as Tree from "../Tree.js";
1
+ import { Tree } from "../internal.js";
2
2
 
3
3
  /**
4
4
  * Return a tree that performs a deep merge of the given trees.
package/src/symbols.js ADDED
@@ -0,0 +1 @@
1
+ export const parent = Symbol("parent");
@@ -1,20 +1,20 @@
1
- import * as Tree from "../Tree.js";
1
+ import { Tree } from "../internal.js";
2
2
 
3
3
  const treeToCaches = new WeakMap();
4
4
 
5
5
  /**
6
- * Given a keyMap, return a new keyMap and inverseKeyMap that cache the results of
7
- * the original keyMap.
6
+ * Given a key function, return a new key function and inverse key function that
7
+ * cache the results of the original.
8
8
  *
9
9
  * @typedef {import("../../index.ts").KeyFn} KeyFn
10
10
  *
11
- * @param {KeyFn} keyMap
11
+ * @param {KeyFn} keyFn
12
12
  * @param {boolean} [deep]
13
- * @returns {{ keyMap: KeyFn, inverseKeyMap: KeyFn }}
13
+ * @returns {{ key: KeyFn, inverseKey: KeyFn }}
14
14
  */
15
- export default function createCachedKeysTransform(keyMap, deep = false) {
15
+ export default function createCachedKeysTransform(keyFn, deep = false) {
16
16
  return {
17
- async inverseKeyMap(resultKey, tree) {
17
+ async inverseKey(resultKey, tree) {
18
18
  const caches = getCachesForTree(tree);
19
19
 
20
20
  // First check to see if we've already computed an source key for this
@@ -37,7 +37,7 @@ export default function createCachedKeysTransform(keyMap, deep = false) {
37
37
  if (deep && (await Tree.isKeyForSubtree(tree, sourceKey))) {
38
38
  computedResultKey = sourceKey;
39
39
  } else {
40
- computedResultKey = await keyMap(sourceKey, tree);
40
+ computedResultKey = await keyFn(sourceKey, tree);
41
41
  }
42
42
 
43
43
  caches.sourceKeyToResultKey.set(sourceKey, computedResultKey);
@@ -52,7 +52,7 @@ export default function createCachedKeysTransform(keyMap, deep = false) {
52
52
  return undefined;
53
53
  },
54
54
 
55
- async keyMap(sourceKey, tree) {
55
+ async key(sourceKey, tree) {
56
56
  const keyMaps = getCachesForTree(tree);
57
57
 
58
58
  // First check to see if we've already computed an result key for this
@@ -62,7 +62,7 @@ export default function createCachedKeysTransform(keyMap, deep = false) {
62
62
  return keyMaps.sourceKeyToResultKey.get(sourceKey);
63
63
  }
64
64
 
65
- const resultKey = await keyMap(sourceKey, tree);
65
+ const resultKey = await keyFn(sourceKey, tree);
66
66
 
67
67
  // Cache the mappings from source key <-> result key for next time.
68
68
  keyMaps.sourceKeyToResultKey.set(sourceKey, resultKey);
@@ -1,5 +1,4 @@
1
- import ObjectTree from "../ObjectTree.js";
2
- import * as Tree from "../Tree.js";
1
+ import { DeepObjectTree, Tree } from "../internal.js";
3
2
 
4
3
  /**
5
4
  * Given a function that returns a grouping key for a value, returns a transform
@@ -11,7 +10,8 @@ export default function createGroupByTransform(groupKeyFn) {
11
10
  /**
12
11
  * @type {import("../../index.ts").TreeTransform}
13
12
  */
14
- return async function groupByTransform(tree) {
13
+ return async function groupByTransform(treelike) {
14
+ const tree = Tree.from(treelike);
15
15
  const result = {};
16
16
  for (const key of await tree.keys()) {
17
17
  const value = await tree.get(key);
@@ -36,6 +36,6 @@ export default function createGroupByTransform(groupKeyFn) {
36
36
  }
37
37
  }
38
38
 
39
- return new ObjectTree(result);
39
+ return new DeepObjectTree(result);
40
40
  };
41
41
  }