@weborigami/async-tree 0.5.7 → 0.6.0

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 (178) hide show
  1. package/browser.js +1 -1
  2. package/index.ts +31 -35
  3. package/main.js +1 -2
  4. package/package.json +4 -7
  5. package/shared.js +77 -12
  6. package/src/Tree.js +8 -2
  7. package/src/drivers/AsyncMap.js +210 -0
  8. package/src/drivers/{BrowserFileTree.js → BrowserFileMap.js} +36 -27
  9. package/src/drivers/{calendarTree.js → CalendarMap.js} +81 -62
  10. package/src/drivers/ConstantMap.js +30 -0
  11. package/src/drivers/DeepObjectMap.js +27 -0
  12. package/src/drivers/{ExplorableSiteTree.js → ExplorableSiteMap.js} +7 -7
  13. package/src/drivers/FileMap.js +245 -0
  14. package/src/drivers/{FunctionTree.js → FunctionMap.js} +19 -22
  15. package/src/drivers/ObjectMap.js +139 -0
  16. package/src/drivers/SetMap.js +13 -0
  17. package/src/drivers/{SiteTree.js → SiteMap.js} +16 -17
  18. package/src/drivers/SyncMap.js +245 -0
  19. package/src/jsonKeys.d.ts +2 -2
  20. package/src/jsonKeys.js +6 -5
  21. package/src/operations/addNextPrevious.js +35 -36
  22. package/src/operations/assign.js +30 -21
  23. package/src/operations/cache.js +29 -35
  24. package/src/operations/cachedKeyFunctions.js +1 -1
  25. package/src/operations/calendar.js +5 -0
  26. package/src/operations/clear.js +13 -12
  27. package/src/operations/constant.js +5 -0
  28. package/src/operations/deepEntries.js +23 -0
  29. package/src/operations/deepMap.js +9 -9
  30. package/src/operations/deepMerge.js +36 -25
  31. package/src/operations/deepReverse.js +23 -16
  32. package/src/operations/deepTake.js +7 -7
  33. package/src/operations/deepText.js +4 -4
  34. package/src/operations/deepValues.js +3 -6
  35. package/src/operations/deepValuesIterator.js +11 -11
  36. package/src/operations/delete.js +8 -12
  37. package/src/operations/entries.js +17 -10
  38. package/src/operations/filter.js +9 -7
  39. package/src/operations/first.js +12 -10
  40. package/src/operations/forEach.js +10 -13
  41. package/src/operations/from.js +31 -39
  42. package/src/operations/globKeys.js +22 -17
  43. package/src/operations/group.js +2 -2
  44. package/src/operations/groupBy.js +24 -22
  45. package/src/operations/has.js +7 -9
  46. package/src/operations/indent.js +2 -2
  47. package/src/operations/inners.js +19 -15
  48. package/src/operations/invokeFunctions.js +22 -10
  49. package/src/operations/isAsyncMutableTree.js +5 -12
  50. package/src/operations/isAsyncTree.js +5 -20
  51. package/src/operations/isMap.js +39 -0
  52. package/src/operations/isMaplike.js +34 -0
  53. package/src/operations/isReadOnlyMap.js +14 -0
  54. package/src/operations/isTraversable.js +3 -3
  55. package/src/operations/isTreelike.js +5 -30
  56. package/src/operations/json.js +4 -12
  57. package/src/operations/keys.js +17 -8
  58. package/src/operations/length.js +9 -8
  59. package/src/operations/map.js +27 -30
  60. package/src/operations/mapExtension.js +20 -16
  61. package/src/operations/mapReduce.js +22 -17
  62. package/src/operations/mask.js +31 -22
  63. package/src/operations/match.js +13 -9
  64. package/src/operations/merge.js +43 -35
  65. package/src/operations/paginate.js +26 -18
  66. package/src/operations/parent.js +7 -7
  67. package/src/operations/paths.js +8 -8
  68. package/src/operations/plain.js +6 -6
  69. package/src/operations/regExpKeys.js +21 -12
  70. package/src/operations/reverse.js +21 -15
  71. package/src/operations/root.js +6 -5
  72. package/src/operations/scope.js +31 -26
  73. package/src/operations/shuffle.js +23 -16
  74. package/src/operations/size.js +13 -0
  75. package/src/operations/sort.js +55 -40
  76. package/src/operations/sync.js +21 -0
  77. package/src/operations/take.js +23 -11
  78. package/src/operations/text.js +4 -4
  79. package/src/operations/toFunction.js +7 -7
  80. package/src/operations/traverse.js +4 -4
  81. package/src/operations/traverseOrThrow.js +13 -9
  82. package/src/operations/traversePath.js +2 -2
  83. package/src/operations/values.js +18 -9
  84. package/src/operations/withKeys.js +22 -16
  85. package/src/symbols.js +1 -0
  86. package/src/utilities/castArraylike.js +10 -2
  87. package/src/utilities/getMapArgument.js +38 -0
  88. package/src/utilities/getParent.js +2 -2
  89. package/src/utilities/isStringlike.js +7 -5
  90. package/src/utilities/setParent.js +7 -7
  91. package/src/utilities/toFunction.js +2 -2
  92. package/src/utilities/toPlainValue.js +22 -18
  93. package/test/SampleAsyncMap.js +34 -0
  94. package/test/browser/assert.js +20 -0
  95. package/test/browser/index.html +54 -21
  96. package/test/drivers/AsyncMap.test.js +119 -0
  97. package/test/drivers/{BrowserFileTree.test.js → BrowserFileMap.test.js} +42 -23
  98. package/test/drivers/{calendarTree.test.js → CalendarMap.test.js} +17 -19
  99. package/test/drivers/ConstantMap.test.js +15 -0
  100. package/test/drivers/DeepObjectMap.test.js +36 -0
  101. package/test/drivers/{ExplorableSiteTree.test.js → ExplorableSiteMap.test.js} +29 -14
  102. package/test/drivers/FileMap.test.js +185 -0
  103. package/test/drivers/FunctionMap.test.js +56 -0
  104. package/test/drivers/ObjectMap.test.js +166 -0
  105. package/test/drivers/SetMap.test.js +35 -0
  106. package/test/drivers/{SiteTree.test.js → SiteMap.test.js} +14 -10
  107. package/test/drivers/SyncMap.test.js +321 -0
  108. package/test/jsonKeys.test.js +2 -2
  109. package/test/operations/addNextPrevious.test.js +3 -2
  110. package/test/operations/assign.test.js +30 -35
  111. package/test/operations/cache.test.js +8 -6
  112. package/test/operations/cachedKeyFunctions.test.js +6 -5
  113. package/test/operations/clear.test.js +6 -27
  114. package/test/operations/deepEntries.test.js +32 -0
  115. package/test/operations/deepMerge.test.js +6 -5
  116. package/test/operations/deepReverse.test.js +2 -2
  117. package/test/operations/deepTake.test.js +2 -2
  118. package/test/operations/deepText.test.js +4 -4
  119. package/test/operations/deepValuesIterator.test.js +2 -2
  120. package/test/operations/delete.test.js +2 -2
  121. package/test/operations/extensionKeyFunctions.test.js +6 -5
  122. package/test/operations/filter.test.js +3 -3
  123. package/test/operations/from.test.js +23 -31
  124. package/test/operations/globKeys.test.js +9 -9
  125. package/test/operations/groupBy.test.js +6 -5
  126. package/test/operations/inners.test.js +4 -4
  127. package/test/operations/invokeFunctions.test.js +2 -2
  128. package/test/operations/isMap.test.js +15 -0
  129. package/test/operations/isMaplike.test.js +15 -0
  130. package/test/operations/json.test.js +2 -2
  131. package/test/operations/keys.test.js +16 -3
  132. package/test/operations/map.test.js +20 -18
  133. package/test/operations/mapExtension.test.js +6 -6
  134. package/test/operations/mapReduce.test.js +2 -2
  135. package/test/operations/mask.test.js +4 -3
  136. package/test/operations/match.test.js +2 -2
  137. package/test/operations/merge.test.js +15 -11
  138. package/test/operations/paginate.test.js +5 -5
  139. package/test/operations/parent.test.js +3 -3
  140. package/test/operations/paths.test.js +6 -6
  141. package/test/operations/plain.test.js +8 -8
  142. package/test/operations/regExpKeys.test.js +12 -11
  143. package/test/operations/reverse.test.js +4 -3
  144. package/test/operations/scope.test.js +6 -5
  145. package/test/operations/shuffle.test.js +3 -2
  146. package/test/operations/sort.test.js +7 -10
  147. package/test/operations/sync.test.js +43 -0
  148. package/test/operations/take.test.js +2 -2
  149. package/test/operations/toFunction.test.js +2 -2
  150. package/test/operations/traverse.test.js +4 -5
  151. package/test/operations/withKeys.test.js +2 -2
  152. package/test/utilities/setParent.test.js +6 -6
  153. package/test/utilities/toFunction.test.js +2 -2
  154. package/test/utilities/toPlainValue.test.js +51 -12
  155. package/src/drivers/DeepMapTree.js +0 -23
  156. package/src/drivers/DeepObjectTree.js +0 -18
  157. package/src/drivers/DeferredTree.js +0 -81
  158. package/src/drivers/FileTree.js +0 -276
  159. package/src/drivers/MapTree.js +0 -70
  160. package/src/drivers/ObjectTree.js +0 -158
  161. package/src/drivers/SetTree.js +0 -34
  162. package/src/drivers/constantTree.js +0 -19
  163. package/src/drivers/limitConcurrency.js +0 -63
  164. package/src/internal.js +0 -16
  165. package/src/utilities/getTreeArgument.js +0 -43
  166. package/test/drivers/DeepMapTree.test.js +0 -17
  167. package/test/drivers/DeepObjectTree.test.js +0 -35
  168. package/test/drivers/DeferredTree.test.js +0 -22
  169. package/test/drivers/FileTree.test.js +0 -192
  170. package/test/drivers/FunctionTree.test.js +0 -46
  171. package/test/drivers/MapTree.test.js +0 -59
  172. package/test/drivers/ObjectTree.test.js +0 -163
  173. package/test/drivers/SetTree.test.js +0 -44
  174. package/test/drivers/constantTree.test.js +0 -13
  175. package/test/drivers/limitConcurrency.test.js +0 -41
  176. package/test/operations/isAsyncMutableTree.test.js +0 -17
  177. package/test/operations/isAsyncTree.test.js +0 -26
  178. package/test/operations/isTreelike.test.js +0 -13
@@ -1,8 +1,9 @@
1
- import ObjectTree from "../drivers/ObjectTree.js";
2
- import getTreeArgument from "../utilities/getTreeArgument.js";
3
- import from from "./from.js";
4
- import isAsyncMutableTree from "./isAsyncMutableTree.js";
5
- import isAsyncTree from "./isAsyncTree.js";
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+ import SyncMap from "../drivers/SyncMap.js";
3
+ import getMapArgument from "../utilities/getMapArgument.js";
4
+ import isMap from "./isMap.js";
5
+ import isReadOnlyMap from "./isReadOnlyMap.js";
6
+ import keys from "./keys.js";
6
7
 
7
8
  /**
8
9
  * Caches values from a source tree in a second cache tree. Cache source tree
@@ -10,60 +11,51 @@ import isAsyncTree from "./isAsyncTree.js";
10
11
  *
11
12
  * If no second tree is supplied, an in-memory value cache is used.
12
13
  *
13
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
14
- * @typedef {import("@weborigami/types").AsyncMutableTree} AsyncMutableTree
15
- * @typedef {import("../../index.ts").Treelike} Treelike
14
+ * @typedef {import("../../index.ts").Maplike} Maplike
16
15
  *
17
- * @param {Treelike} sourceTreelike
18
- * @param {AsyncMutableTree} [cacheTreelike]
19
- * @returns {Promise}
16
+ * @param {Maplike} sourceMaplike
17
+ * @param {Maplike} [cacheMaplike]
18
+ * @returns {Promise<SyncMap|AsyncMap>}
20
19
  */
21
- export default async function treeCache(sourceTreelike, cacheTreelike) {
22
- const source = await getTreeArgument(sourceTreelike, "cache", {
20
+ export default async function treeCache(sourceMaplike, cacheMaplike) {
21
+ const source = await getMapArgument(sourceMaplike, "cache", {
23
22
  position: 0,
24
23
  });
25
24
 
26
- /** @type {AsyncMutableTree} */
27
25
  let cache;
28
- if (cacheTreelike) {
26
+ if (cacheMaplike) {
29
27
  cache = /** @type {any} */ (
30
- await getTreeArgument(cacheTreelike, "cache", { position: 1 })
28
+ await getMapArgument(cacheMaplike, "cache", { position: 1 })
31
29
  );
32
30
  // @ts-ignore
33
- if (!isAsyncMutableTree(cache)) {
34
- throw new Error("Cache tree must define a set() method.");
31
+ if (isReadOnlyMap(cache)) {
32
+ throw new Error("cache: Cache tree can't be read-only.");
35
33
  }
36
34
  } else {
37
- cache = new ObjectTree({});
35
+ cache = new SyncMap();
38
36
  }
39
37
 
40
- let keys;
41
- return {
38
+ let sourceKeys;
39
+
40
+ return Object.assign(new AsyncMap(), {
42
41
  description: "cache",
43
42
 
44
43
  async get(key) {
45
44
  // Check cache tree first.
46
45
  let cacheValue = await cache.get(key);
47
- if (cacheValue !== undefined && !isAsyncTree(cacheValue)) {
46
+ if (cacheValue !== undefined && !isMap(cacheValue)) {
48
47
  // Leaf node cache hit
49
48
  return cacheValue;
50
49
  }
51
50
 
52
51
  // Cache miss or interior node cache hit.
53
52
  let value = await source.get(key);
54
- if (isAsyncTree(value)) {
53
+ if (isMap(value)) {
55
54
  // Construct merged tree for a tree result.
56
55
  if (cacheValue === undefined) {
57
56
  // Construct new empty container in cache
58
- await cache.set(key, {});
57
+ await cache.set(key, cache.constructor.EMPTY);
59
58
  cacheValue = await cache.get(key);
60
- if (!isAsyncTree(cacheValue)) {
61
- // Coerce to tree and then save it back to the cache. This is
62
- // necessary, e.g., if cache is an ObjectTree; we want the
63
- // subtree to also be an ObjectTree, not a plain object.
64
- cacheValue = from(cacheValue);
65
- await cache.set(key, cacheValue);
66
- }
67
59
  }
68
60
  value = treeCache(value, cacheValue);
69
61
  } else if (value !== undefined) {
@@ -74,9 +66,11 @@ export default async function treeCache(sourceTreelike, cacheTreelike) {
74
66
  return value;
75
67
  },
76
68
 
77
- async keys() {
78
- keys ??= await source.keys();
79
- return keys;
69
+ async *keys() {
70
+ sourceKeys ??= await keys(source);
71
+ yield* sourceKeys;
80
72
  },
81
- };
73
+
74
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
75
+ });
82
76
  }
@@ -35,7 +35,7 @@ export default function cachedKeyFunctions(keyFn, deep = false) {
35
35
  // final match. This is O(n), but we stop as soon as we find a match,
36
36
  // and subsequent calls will benefit from the intermediate results.
37
37
  const resultKeyWithoutSlash = trailingSlash.remove(resultKey);
38
- for (const sourceKey of await tree.keys()) {
38
+ for await (const sourceKey of tree.keys()) {
39
39
  // Skip any source keys we already know about.
40
40
  if (sourceKeyToResultKey.has(sourceKey)) {
41
41
  continue;
@@ -0,0 +1,5 @@
1
+ import CalendarMap from "../drivers/CalendarMap.js";
2
+
3
+ export default function calendar(...args) {
4
+ return new CalendarMap(...args);
5
+ }
@@ -1,20 +1,21 @@
1
- import from from "./from.js";
2
- import isAsyncMutableTree from "./isAsyncMutableTree.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
3
2
 
4
3
  /**
5
- * Remove all entries from the tree.
4
+ * Remove all entries from the map.
6
5
  *
7
- * @typedef {import("../../index.ts").Treelike} Treelike
6
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
7
  *
9
- * @param {Treelike} treelike
8
+ * @param {Maplike} maplike
10
9
  */
11
- export default async function clear(treelike) {
12
- const tree = from(treelike);
13
- if (!isAsyncMutableTree(tree)) {
14
- throw new TypeError("clear: can't clear a read-only tree.");
10
+ export default async function clear(maplike) {
11
+ const map = await getMapArgument(maplike, "clear");
12
+ if ("readOnly" in map && map.readOnly) {
13
+ throw new TypeError("clear: target map is read-only");
14
+ }
15
+ const promises = [];
16
+ for await (const key of map.keys()) {
17
+ promises.push(map.delete(key));
15
18
  }
16
- const keys = Array.from(await tree.keys());
17
- const promises = keys.map((key) => tree.set(key, undefined));
18
19
  await Promise.all(promises);
19
- return tree;
20
+ return map;
20
21
  }
@@ -0,0 +1,5 @@
1
+ import ConstantMap from "../drivers/ConstantMap.js";
2
+
3
+ export default function constant(value) {
4
+ return new ConstantMap(value);
5
+ }
@@ -0,0 +1,23 @@
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
+ import entries from "./entries.js";
3
+ import isMap from "./isMap.js";
4
+
5
+ /**
6
+ * Return the deep nested entries in the tree as arrays of [key, value] pairs.
7
+ *
8
+ * @typedef {import("../../index.ts").Maplike} Maplike
9
+ *
10
+ * @param {Maplike} maplike
11
+ */
12
+ export default async function deepEntries(maplike) {
13
+ const tree = await getMapArgument(maplike, "deepEntries");
14
+
15
+ const treeEntries = await entries(tree);
16
+ const result = await Promise.all(
17
+ treeEntries.map(async ([key, value]) => {
18
+ const resolvedValue = isMap(value) ? await deepEntries(value) : value;
19
+ return [key, resolvedValue];
20
+ })
21
+ );
22
+ return result;
23
+ }
@@ -1,21 +1,21 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
  import isPlainObject from "../utilities/isPlainObject.js";
3
3
  import map from "./map.js";
4
4
 
5
5
  /**
6
6
  * Shorthand for calling `map` with the `deep: true` option.
7
7
  *
8
- * @typedef {import("../../index.ts").TreeMapOptions} TreeMapOptions
9
- * @typedef {import("../../index.ts").Treelike} Treelike
8
+ * @typedef {import("../../index.ts").MapOptions} MapOptions
9
+ * @typedef {import("../../index.ts").Maplike} Maplike
10
10
  * @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
11
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
11
+ * @typedef {import("../../index.ts").AsyncMap} AsyncMap
12
12
  *
13
- * @param {Treelike} treelike
14
- * @param {ValueKeyFn|TreeMapOptions} options
15
- * @returns {Promise<AsyncTree>}
13
+ * @param {Maplike} maplike
14
+ * @param {ValueKeyFn|MapOptions} options
15
+ * @returns {Promise<AsyncMap>}
16
16
  */
17
- export default async function deepMap(treelike, options) {
18
- const tree = await getTreeArgument(treelike, "deepMap", { deep: true });
17
+ export default async function deepMap(maplike, options) {
18
+ const tree = await getMapArgument(maplike, "deepMap", { deep: true });
19
19
  const withDeep = isPlainObject(options)
20
20
  ? // Dictionary
21
21
  { ...options, deep: true }
@@ -1,35 +1,42 @@
1
+ import AsyncMap from "../drivers/AsyncMap.js";
1
2
  import * as trailingSlash from "../trailingSlash.js";
3
+ import isUnpackable from "../utilities/isUnpackable.js";
2
4
  import from from "./from.js";
3
- import isAsyncTree from "./isAsyncTree.js";
4
- import isTreelike from "./isTreelike.js";
5
+ import isMap from "./isMap.js";
6
+ import isMaplike from "./isMaplike.js";
7
+ import keys from "./keys.js";
5
8
 
6
9
  /**
7
10
  * Return a tree that performs a deep merge of the given trees.
8
11
  *
9
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
10
- * @typedef {import("../../index.ts").Treelike} Treelike
12
+ * @typedef {import("../../index.ts").Maplike} Maplike
11
13
  *
12
- * @param {Treelike[]} sources
13
- * @returns {AsyncTree & { description: string }}
14
+ * @param {Maplike[]} maplikes
15
+ * @returns {Promise<AsyncMap>}
14
16
  */
15
- export default function deepMerge(...sources) {
16
- const filtered = sources.filter((source) => source);
17
- let trees = filtered.map((treelike) => from(treelike, { deep: true }));
17
+ export default async function deepMerge(...maplikes) {
18
+ const filtered = maplikes.filter((source) => source);
19
+ const unpacked = await Promise.all(
20
+ filtered.map(async (source) =>
21
+ isUnpackable(source) ? await source.unpack() : source
22
+ )
23
+ );
24
+ const sources = unpacked.map((maplike) => from(maplike, { deep: true }));
18
25
 
19
- return {
26
+ return Object.assign(new AsyncMap(), {
20
27
  description: "deepMerge",
21
28
 
22
29
  async get(key) {
23
30
  const subtrees = [];
24
31
 
25
32
  // Check trees for the indicated key in reverse order.
26
- for (let index = trees.length - 1; index >= 0; index--) {
27
- const tree = trees[index];
28
- const value = await tree.get(key);
29
- if (
30
- isAsyncTree(value) ||
31
- (isTreelike(value) && trailingSlash.has(key))
32
- ) {
33
+ for (let index = sources.length - 1; index >= 0; index--) {
34
+ const source = sources[index];
35
+ const normalized = /** @type {any} */ (source).trailingSlashKeys
36
+ ? key
37
+ : trailingSlash.remove(key);
38
+ const value = await source.get(normalized);
39
+ if (isMap(value) || (isMaplike(value) && trailingSlash.has(key))) {
33
40
  subtrees.unshift(value);
34
41
  } else if (value !== undefined) {
35
42
  return value;
@@ -46,21 +53,25 @@ export default function deepMerge(...sources) {
46
53
  }
47
54
  },
48
55
 
49
- async keys() {
50
- const keys = new Set();
56
+ async *keys() {
57
+ const treeKeys = new Set();
51
58
  // Collect keys in the order the trees were provided.
52
- for (const tree of trees) {
53
- for (const key of await tree.keys()) {
59
+ for (const tree of sources) {
60
+ for (const key of await keys(tree)) {
54
61
  // Remove the alternate form of the key (if it exists)
55
62
  const alternateKey = trailingSlash.toggle(key);
56
63
  if (alternateKey !== key) {
57
- keys.delete(alternateKey);
64
+ treeKeys.delete(alternateKey);
58
65
  }
59
66
 
60
- keys.add(key);
67
+ treeKeys.add(key);
61
68
  }
62
69
  }
63
- return keys;
70
+ yield* treeKeys;
64
71
  },
65
- };
72
+
73
+ sources,
74
+
75
+ trailingSlashKeys: true,
76
+ });
66
77
  }
@@ -1,31 +1,38 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
2
- import isAsyncTree from "./isAsyncTree.js";
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+ import getMapArgument from "../utilities/getMapArgument.js";
3
+ import isMap from "./isMap.js";
4
+ import keys from "./keys.js";
3
5
 
4
6
  /**
5
7
  * Reverse the order of keys at all levels of the tree.
6
8
  *
7
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
8
- * @typedef {import("../../index.ts").Treelike} Treelike
9
+ * @typedef {import("../../index.ts").Maplike} Maplike
9
10
  *
10
- * @param {Treelike} treelike
11
- * @returns {Promise<AsyncTree>}
11
+ * @param {Maplike} maplike
12
+ * @returns {Promise<AsyncMap>}
12
13
  */
13
- export default async function deepReverse(treelike) {
14
- const tree = await getTreeArgument(treelike, "deepReverse", { deep: true });
14
+ export default async function deepReverse(maplike) {
15
+ const source = await getMapArgument(maplike, "deepReverse", { deep: true });
16
+
17
+ return Object.assign(new AsyncMap(), {
18
+ description: "deepReverse",
15
19
 
16
- return {
17
20
  async get(key) {
18
- let value = await tree.get(key);
19
- if (isAsyncTree(value)) {
21
+ let value = await source.get(key);
22
+ if (isMap(value)) {
20
23
  value = deepReverse(value);
21
24
  }
22
25
  return value;
23
26
  },
24
27
 
25
- async keys() {
26
- const keys = Array.from(await tree.keys());
27
- keys.reverse();
28
- return keys;
28
+ async *keys() {
29
+ const treeKeys = await keys(source);
30
+ treeKeys.reverse();
31
+ yield* treeKeys;
29
32
  },
30
- };
33
+
34
+ source,
35
+
36
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
37
+ });
31
38
  }
@@ -1,6 +1,6 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
  import from from "./from.js";
3
- import isAsyncTree from "./isAsyncTree.js";
3
+ import isMap from "./isMap.js";
4
4
 
5
5
  /**
6
6
  * Returns a function that traverses a tree deeply and returns the values of the
@@ -9,23 +9,23 @@ import isAsyncTree from "./isAsyncTree.js";
9
9
  * This is similar to `deepValues`, but it is more efficient for large trees as
10
10
  * stops after `count` values.
11
11
  *
12
- * @param {import("../../index.ts").Treelike} treelike
12
+ * @param {import("../../index.ts").Maplike} maplike
13
13
  * @param {number} count
14
14
  */
15
- export default async function deepTake(treelike, count) {
16
- const tree = await getTreeArgument(treelike, "deepTake", { deep: true });
15
+ export default async function deepTake(maplike, count) {
16
+ const tree = await getMapArgument(maplike, "deepTake", { deep: true });
17
17
  const { values } = await traverse(tree, count);
18
18
  return from(values, { deep: true });
19
19
  }
20
20
 
21
21
  async function traverse(tree, count) {
22
22
  const values = [];
23
- for (const key of await tree.keys()) {
23
+ for await (const key of tree.keys()) {
24
24
  if (count <= 0) {
25
25
  break;
26
26
  }
27
27
  let value = await tree.get(key);
28
- if (isAsyncTree(value)) {
28
+ if (isMap(value)) {
29
29
  const traversed = await traverse(value, count);
30
30
  values.push(...traversed.values);
31
31
  count = traversed.count;
@@ -1,14 +1,14 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
  import toString from "../utilities/toString.js";
3
3
  import deepValuesIterator from "./deepValuesIterator.js";
4
4
 
5
5
  /**
6
6
  * Concatenate the deep text values in a tree.
7
7
  *
8
- * @param {import("../../index.ts").Treelike} treelike
8
+ * @param {import("../../index.ts").Maplike} maplike
9
9
  */
10
- export default async function deepText(treelike) {
11
- const tree = await getTreeArgument(treelike, "deepText", { deep: true });
10
+ export default async function deepText(maplike) {
11
+ const tree = await getMapArgument(maplike, "deepText", { deep: true });
12
12
  const strings = [];
13
13
  for await (const value of deepValuesIterator(tree, { expand: true })) {
14
14
  let string;
@@ -3,14 +3,11 @@ import deepValuesIterator from "./deepValuesIterator.js";
3
3
  /**
4
4
  * Return the in-order exterior values of a tree as a flat array.
5
5
  *
6
- * @param {import("../../index.ts").Treelike} treelike
6
+ * @param {import("../../index.ts").Maplike} maplike
7
7
  * @param {{ expand?: boolean }} [options]
8
8
  */
9
- export default async function deepValues(
10
- treelike,
11
- options = { expand: false }
12
- ) {
13
- const iterator = deepValuesIterator(treelike, options);
9
+ export default async function deepValues(maplike, options = { expand: false }) {
10
+ const iterator = deepValuesIterator(maplike, options);
14
11
  const values = [];
15
12
  for await (const value of iterator) {
16
13
  values.push(value);
@@ -1,32 +1,32 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
2
- import isAsyncTree from "./isAsyncTree.js";
3
- import isTreelike from "./isTreelike.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
+ import isMap from "./isMap.js";
3
+ import isMaplike from "./isMaplike.js";
4
4
 
5
5
  /**
6
6
  * Return an iterator that yields all values in a tree, including nested trees.
7
7
  *
8
- * If the `expand` option is true, treelike values (but not functions) will be
8
+ * If the `expand` option is true, maplike values (but not functions) will be
9
9
  * expanded into nested trees and their values will be yielded.
10
10
  *
11
- * @param {import("../../index.ts").Treelike} treelike
11
+ * @param {import("../../index.ts").Maplike} maplike
12
12
  * @param {{ expand?: boolean }} [options]
13
13
  * @returns {AsyncGenerator<any, void, undefined>}
14
14
  */
15
15
  export default async function* deepValuesIterator(
16
- treelike,
16
+ maplike,
17
17
  options = { expand: false }
18
18
  ) {
19
- const tree = await getTreeArgument(treelike, "deepValuesIterator", {
19
+ const tree = await getMapArgument(maplike, "deepValuesIterator", {
20
20
  deep: true,
21
21
  });
22
22
 
23
- for (const key of await tree.keys()) {
24
- let value = await tree.get(key);
23
+ for await (const key of tree.keys()) {
24
+ const value = await tree.get(key);
25
25
 
26
26
  // Recurse into child trees, but don't expand functions.
27
27
  const recurse =
28
- isAsyncTree(value) ||
29
- (options.expand && typeof value !== "function" && isTreelike(value));
28
+ isMap(value) ||
29
+ (options.expand && typeof value !== "function" && isMaplike(value));
30
30
  if (recurse) {
31
31
  yield* deepValuesIterator(value, options);
32
32
  } else {
@@ -1,20 +1,16 @@
1
- import has from "./has.js";
2
-
3
1
  /**
4
2
  * Removes the value for the given key from the specific node of the tree.
5
3
  *
6
- * @typedef {import("@weborigami/types").AsyncMutableTree} AsyncMutableTree
4
+ * @typedef {import("../../index.ts").Maplike} Maplike
7
5
  *
8
- * @param {AsyncMutableTree} tree
6
+ * @param {Maplike} maplike
9
7
  * @param {any} key
10
8
  */
9
+
10
+ import getMapArgument from "../utilities/getMapArgument.js";
11
+
11
12
  // `delete` is a reserved word in JavaScript, so we use `del` instead.
12
- export default async function del(tree, key) {
13
- const exists = await has(tree, key);
14
- if (exists) {
15
- await tree.set(key, undefined);
16
- return true;
17
- } else {
18
- return false;
19
- }
13
+ export default async function del(maplike, key) {
14
+ const map = await getMapArgument(maplike, "delete");
15
+ return map.delete(key);
20
16
  }
@@ -1,16 +1,23 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
 
3
3
  /**
4
- * Returns a new `Iterator` object that contains a two-member array of `[key,
5
- * value]` for each element in the specific node of the tree.
4
+ * Returns an array of `[key, value]` for each entry in the map.
6
5
  *
7
- * @typedef {import("../../index.ts").Treelike} Treelike
6
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
7
  *
9
- * @param {Treelike} treelike
8
+ * @param {Maplike} maplike
9
+ * @returns {Promise<Array<[any, any]>>}
10
10
  */
11
- export default async function entries(treelike) {
12
- const tree = await getTreeArgument(treelike, "entries");
13
- const keys = Array.from(await tree.keys());
14
- const promises = keys.map(async (key) => [key, await tree.get(key)]);
15
- return Promise.all(promises);
11
+ export default async function entries(maplike) {
12
+ const map = await getMapArgument(maplike, "entries");
13
+ if (map instanceof Map) {
14
+ return Array.from(map.entries());
15
+ } else {
16
+ // AsyncMap
17
+ const result = [];
18
+ for await (const entry of map) {
19
+ result.push(entry);
20
+ }
21
+ return result;
22
+ }
16
23
  }
@@ -1,18 +1,18 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
  import map from "./map.js";
3
3
 
4
4
  /**
5
5
  * Given a tree an a test function, return a new tree whose keys correspond to
6
6
  * the values that pass the test function.
7
7
  *
8
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
9
- * @typedef {import("../../index.ts").Treelike} Treelike
8
+ * @typedef {import("../../index.ts").Maplike} Maplike
9
+ * @typedef {import("../../index.ts").AsyncMap} AsyncMap
10
10
  *
11
- * @param {Treelike} treelike
11
+ * @param {Maplike} maplike
12
12
  * @param {function|any} options
13
- * @returns {Promise<AsyncTree>}
13
+ * @returns {Promise<AsyncMap>}
14
14
  */
15
- export default async function filter(treelike, options) {
15
+ export default async function filter(maplike, options) {
16
16
  let testFn;
17
17
  let deep;
18
18
  if (typeof options === "function") {
@@ -23,10 +23,12 @@ export default async function filter(treelike, options) {
23
23
  deep = options.deep ?? false;
24
24
  }
25
25
 
26
- const tree = await getTreeArgument(treelike, "filter", { deep });
26
+ const tree = await getMapArgument(maplike, "filter", { deep });
27
27
  return map(tree, {
28
28
  deep,
29
29
 
30
+ description: "filter",
31
+
30
32
  // Assume source key is the same as result key
31
33
  inverseKey: async (resultKey) => resultKey,
32
34
 
@@ -1,18 +1,20 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
 
3
3
  /**
4
4
  * Return the first value in the tree.
5
5
  *
6
- * @typedef {import("../../index.ts").Treelike} Treelike
6
+ * @typedef {import("../../index.ts").Maplike} Maplike
7
7
  *
8
- * @param {Treelike} treelike
8
+ * @param {Maplike} maplike
9
9
  */
10
- export default async function first(treelike) {
11
- const tree = await getTreeArgument(treelike, "first");
12
- for (const key of await tree.keys()) {
13
- // Just return first value immediately.
14
- const value = await tree.get(key);
15
- return value;
10
+ export default async function first(maplike) {
11
+ const map = await getMapArgument(maplike, "first");
12
+ let firstKey;
13
+ for await (const key of map.keys()) {
14
+ // Just needed to get first key
15
+ firstKey = key;
16
+ break;
16
17
  }
17
- return undefined;
18
+ const value = firstKey ? await map.get(firstKey) : undefined;
19
+ return value;
18
20
  }